Master Key Migration Project
[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     memcpy(tempprinc, inprinc, sizeof(krb5_principal_data));
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     kadm5_principal_ent_rec     entry_local, *entry_orig;
749
750     CHECK_HANDLE(server_handle);
751
752     krb5_clear_error_message(handle->context);
753
754     /*
755      * In version 1, all the defined fields are always returned.
756      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
757      * filled with allocated memory.
758      */
759     if (handle->api_version == KADM5_API_VERSION_1) {
760          mask = KADM5_PRINCIPAL_NORMAL_MASK;
761          entry_orig = entry;
762          entry = &entry_local;
763     } else {
764          mask = in_mask;
765     }
766
767     memset((char *) entry, 0, sizeof(*entry));
768
769     if (principal == NULL)
770         return EINVAL;
771
772     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
773         return ret;
774
775     if ((mask & KADM5_POLICY) &&
776         adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
777         if ((entry->policy = strdup(adb.policy)) == NULL) {
778             ret = ENOMEM;
779             goto done;
780         }
781     }
782
783     if (mask & KADM5_AUX_ATTRIBUTES)
784          entry->aux_attributes = adb.aux_attributes;
785
786     if ((mask & KADM5_PRINCIPAL) &&
787         (ret = krb5_copy_principal(handle->context, kdb.princ,
788                                    &entry->principal))) {
789         goto done;
790     }
791
792     if (mask & KADM5_PRINC_EXPIRE_TIME)
793          entry->princ_expire_time = kdb.expiration;
794
795     if ((mask & KADM5_LAST_PWD_CHANGE) &&
796         (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
797                                                &(entry->last_pwd_change)))) {
798         goto done;
799     }
800
801     if (mask & KADM5_PW_EXPIRATION)
802          entry->pw_expiration = kdb.pw_expiration;
803     if (mask & KADM5_MAX_LIFE)
804          entry->max_life = kdb.max_life;
805
806     /* this is a little non-sensical because the function returns two */
807     /* values that must be checked separately against the mask */
808     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
809         ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
810                                              &(entry->mod_date),
811                                              &(entry->mod_name));
812         if (ret) {
813             goto done;
814         }
815
816         if (! (mask & KADM5_MOD_TIME))
817             entry->mod_date = 0;
818         if (! (mask & KADM5_MOD_NAME)) {
819             krb5_free_principal(handle->context, entry->principal);
820             entry->principal = NULL;
821         }
822     }
823
824     if (mask & KADM5_ATTRIBUTES)
825          entry->attributes = kdb.attributes;
826
827     if (mask & KADM5_KVNO)
828          for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
829               if (kdb.key_data[i].key_data_kvno > entry->kvno)
830                    entry->kvno = kdb.key_data[i].key_data_kvno;
831
832     ret = krb5_dbe_lookup_mkvno(handle->context, &kdb, &entry->mkvno);
833     if (ret)
834         goto done;
835
836     /*
837      * It's my understanding that KADM5_API_VERSION_1 is for OpenVision admin
838      * system compatiblity and is not required to maintain at this point so I'm
839      * commenting out this code.
840      * -- Will Fiveash
841      */
842 #if 0 /************** Begin IFDEF'ed OUT *******************************/
843     if (handle->api_version == KADM5_API_VERSION_2)
844          entry->mkvno = 0;
845     else {
846          /* XXX I'll be damned if I know how to deal with this one --marc */
847          entry->mkvno = 1;
848     }
849 #endif /**************** END IFDEF'ed OUT *******************************/
850
851     /*
852      * The new fields that only exist in version 2 start here
853      */
854     if (handle->api_version == KADM5_API_VERSION_2) {
855          if (mask & KADM5_MAX_RLIFE)
856               entry->max_renewable_life = kdb.max_renewable_life;
857          if (mask & KADM5_LAST_SUCCESS)
858               entry->last_success = kdb.last_success;
859          if (mask & KADM5_LAST_FAILED)
860               entry->last_failed = kdb.last_failed;
861          if (mask & KADM5_FAIL_AUTH_COUNT)
862               entry->fail_auth_count = kdb.fail_auth_count;
863          if (mask & KADM5_TL_DATA) {
864               krb5_tl_data *tl, *tl2;
865
866               entry->tl_data = NULL;
867
868               tl = kdb.tl_data;
869               while (tl) {
870                    if (tl->tl_data_type > 255) {
871                         if ((tl2 = dup_tl_data(tl)) == NULL) {
872                              ret = ENOMEM;
873                              goto done;
874                         }
875                         tl2->tl_data_next = entry->tl_data;
876                         entry->tl_data = tl2;
877                         entry->n_tl_data++;
878                    }
879
880                    tl = tl->tl_data_next;
881               }
882          }
883          if (mask & KADM5_KEY_DATA) {
884               entry->n_key_data = kdb.n_key_data;
885               if(entry->n_key_data) {
886                       entry->key_data = (krb5_key_data *)
887                               malloc(entry->n_key_data*sizeof(krb5_key_data));
888                       if (entry->key_data == NULL) {
889                               ret = ENOMEM;
890                               goto done;
891                       }
892               } else
893                       entry->key_data = NULL;
894
895               for (i = 0; i < entry->n_key_data; i++)
896                   ret = krb5_copy_key_data_contents(handle->context,
897                                                     &kdb.key_data[i],
898                                                     &entry->key_data[i]);
899                    if (ret)
900                         goto done;
901          }
902     }
903
904     /*
905      * If KADM5_API_VERSION_1, we return an allocated structure, and
906      * we need to convert the new structure back into the format the
907      * caller is expecting.
908      */
909     if (handle->api_version == KADM5_API_VERSION_1) {
910          kadm5_principal_ent_t_v1 newv1;
911
912          newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
913          if (newv1 == NULL) {
914               ret = ENOMEM;
915               goto done;
916          }
917
918          newv1->principal = entry->principal;
919          newv1->princ_expire_time = entry->princ_expire_time;
920          newv1->last_pwd_change = entry->last_pwd_change;
921          newv1->pw_expiration = entry->pw_expiration;
922          newv1->max_life = entry->max_life;
923          newv1->mod_name = entry->mod_name;
924          newv1->mod_date = entry->mod_date;
925          newv1->attributes = entry->attributes;
926          newv1->kvno = entry->kvno;
927          newv1->mkvno = entry->mkvno;
928          newv1->policy = entry->policy;
929          newv1->aux_attributes = entry->aux_attributes;
930
931          *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
932     }
933
934     ret = KADM5_OK;
935
936 done:
937     if (ret && entry->principal)
938          krb5_free_principal(handle->context, entry->principal);
939     kdb_free_entry(handle, &kdb, &adb);
940
941     return ret;
942 }
943
944 /*
945  * Function: check_pw_reuse
946  *
947  * Purpose: Check if a key appears in a list of keys, in order to
948  * enforce password history.
949  *
950  * Arguments:
951  *
952  *      context                 (r) the krb5 context
953  *      hist_keyblock           (r) the key that hist_key_data is
954  *                              encrypted in
955  *      n_new_key_data          (r) length of new_key_data
956  *      new_key_data            (r) keys to check against
957  *                              pw_hist_data, encrypted in hist_keyblock
958  *      n_pw_hist_data          (r) length of pw_hist_data
959  *      pw_hist_data            (r) passwords to check new_key_data against
960  *
961  * Effects:
962  * For each new_key in new_key_data:
963  *      decrypt new_key with the master_keyblock
964  *      for each password in pw_hist_data:
965  *              for each hist_key in password:
966  *                      decrypt hist_key with hist_keyblock
967  *                      compare the new_key and hist_key
968  *
969  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
970  * new_key_data is the same as a key in pw_hist_data, or 0.
971  */
972 static kadm5_ret_t
973 check_pw_reuse(krb5_context context,
974                krb5_keyblock *mkey,
975                krb5_keyblock *hist_keyblock,
976                int n_new_key_data, krb5_key_data *new_key_data,
977                unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
978 {
979     int x, y, z;
980     krb5_keyblock newkey, histkey;
981     krb5_error_code ret;
982
983     for (x = 0; x < n_new_key_data; x++) {
984         ret = krb5_dbekd_decrypt_key_data(context,
985                                           mkey,
986                                           &(new_key_data[x]),
987                                           &newkey, NULL);
988         if (ret)
989             return(ret);
990         for (y = 0; y < n_pw_hist_data; y++) {
991              for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
992                  ret = krb5_dbekd_decrypt_key_data(context,
993                                                    hist_keyblock,
994                                                    &pw_hist_data[y].key_data[z],
995                                                    &histkey, NULL);
996                  if (ret)
997                      return(ret);
998
999                  if ((newkey.length == histkey.length) &&
1000                      (newkey.enctype == histkey.enctype) &&
1001                      (memcmp(newkey.contents, histkey.contents,
1002                              histkey.length) == 0)) {
1003                      krb5_free_keyblock_contents(context, &histkey);
1004                      krb5_free_keyblock_contents(context, &newkey);
1005
1006                      return(KADM5_PASS_REUSE);
1007                  }
1008                  krb5_free_keyblock_contents(context, &histkey);
1009              }
1010         }
1011         krb5_free_keyblock_contents(context, &newkey);
1012     }
1013
1014     return(0);
1015 }
1016
1017 /*
1018  * Function: create_history_entry
1019  *
1020  * Purpose: Creates a password history entry from an array of
1021  * key_data.
1022  *
1023  * Arguments:
1024  *
1025  *      context         (r) krb5_context to use
1026  *      n_key_data      (r) number of elements in key_data
1027  *      key_data        (r) keys to add to the history entry
1028  *      hist            (w) history entry to fill in
1029  *
1030  * Effects:
1031  *
1032  * hist->key_data is allocated to store n_key_data key_datas.  Each
1033  * element of key_data is decrypted with master_keyblock, re-encrypted
1034  * in hist_key, and added to hist->key_data.  hist->n_key_data is
1035  * set to n_key_data.
1036  */
1037 static
1038 int create_history_entry(krb5_context context, krb5_keyblock *mkey, int n_key_data,
1039                          krb5_key_data *key_data, osa_pw_hist_ent *hist)
1040 {
1041      int i, ret;
1042      krb5_keyblock key;
1043      krb5_keysalt salt;
1044
1045      hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
1046      if (hist->key_data == NULL)
1047           return ENOMEM;
1048      memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
1049
1050      for (i = 0; i < n_key_data; i++) {
1051          ret = krb5_dbekd_decrypt_key_data(context,
1052                                            mkey,
1053                                            &key_data[i],
1054                                            &key, &salt);
1055          if (ret)
1056              return ret;
1057
1058          ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
1059                                            &key, &salt,
1060                                            key_data[i].key_data_kvno,
1061                                            &hist->key_data[i]);
1062          if (ret)
1063              return ret;
1064
1065          krb5_free_keyblock_contents(context, &key);
1066          /* krb5_free_keysalt(context, &salt); */
1067      }
1068
1069      hist->n_key_data = n_key_data;
1070      return 0;
1071 }
1072
1073 static
1074 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
1075 {
1076      int i;
1077
1078      for (i = 0; i < hist->n_key_data; i++)
1079           krb5_free_key_data_contents(context, &hist->key_data[i]);
1080      free(hist->key_data);
1081 }
1082
1083 /*
1084  * Function: add_to_history
1085  *
1086  * Purpose: Adds a password to a principal's password history.
1087  *
1088  * Arguments:
1089  *
1090  *      context         (r) krb5_context to use
1091  *      adb             (r/w) admin principal entry to add keys to
1092  *      pol             (r) adb's policy
1093  *      pw              (r) keys for the password to add to adb's key history
1094  *
1095  * Effects:
1096  *
1097  * add_to_history adds a single password to adb's password history.
1098  * pw contains n_key_data keys in its key_data, in storage should be
1099  * allocated but not freed by the caller (XXX blech!).
1100  *
1101  * This function maintains adb->old_keys as a circular queue.  It
1102  * starts empty, and grows each time this function is called until it
1103  * is pol->pw_history_num items long.  adb->old_key_len holds the
1104  * number of allocated entries in the array, and must therefore be [0,
1105  * pol->pw_history_num).  adb->old_key_next is the index into the
1106  * array where the next element should be written, and must be [0,
1107  * adb->old_key_len).
1108  */
1109 static kadm5_ret_t add_to_history(krb5_context context,
1110                                   osa_princ_ent_t adb,
1111                                   kadm5_policy_ent_t pol,
1112                                   osa_pw_hist_ent *pw)
1113 {
1114      osa_pw_hist_ent *histp;
1115      uint32_t nhist;
1116      unsigned int i, knext, nkeys;
1117
1118      nhist = pol->pw_history_num;
1119      /* A history of 1 means just check the current password */
1120      if (nhist <= 1)
1121           return 0;
1122
1123      nkeys = adb->old_key_len;
1124      knext = adb->old_key_next;
1125      /* resize the adb->old_keys array if necessary */
1126      if (nkeys + 1 < nhist) {
1127           if (adb->old_keys == NULL) {
1128                adb->old_keys = (osa_pw_hist_ent *)
1129                     malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
1130           } else {
1131                adb->old_keys = (osa_pw_hist_ent *)
1132                     realloc(adb->old_keys,
1133                             (nkeys + 1) * sizeof (osa_pw_hist_ent));
1134           }
1135           if (adb->old_keys == NULL)
1136                return(ENOMEM);
1137
1138           memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
1139           nkeys = ++adb->old_key_len;
1140           /*
1141            * To avoid losing old keys, shift forward each entry after
1142            * knext.
1143            */
1144           for (i = nkeys - 1; i > knext; i--) {
1145               adb->old_keys[i] = adb->old_keys[i - 1];
1146           }
1147           memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
1148      } else if (nkeys + 1 > nhist) {
1149          /*
1150           * The policy must have changed!  Shrink the array.
1151           * Can't simply realloc() down, since it might be wrapped.
1152           * To understand the arithmetic below, note that we are
1153           * copying into new positions 0 .. N-1 from old positions
1154           * old_key_next-N .. old_key_next-1, modulo old_key_len,
1155           * where N = pw_history_num - 1 is the length of the
1156           * shortened list.        Matt Crawford, FNAL
1157           */
1158          /*
1159           * M = adb->old_key_len, N = pol->pw_history_num - 1
1160           *
1161           * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
1162           */
1163          int j;
1164          osa_pw_hist_t tmp;
1165
1166          tmp = (osa_pw_hist_ent *)
1167              malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
1168          if (tmp == NULL)
1169              return ENOMEM;
1170          for (i = 0; i < nhist - 1; i++) {
1171              /*
1172               * Add nkeys once before taking remainder to avoid
1173               * negative values.
1174               */
1175              j = (i + nkeys + knext - (nhist - 1)) % nkeys;
1176              tmp[i] = adb->old_keys[j];
1177          }
1178          /* Now free the ones we don't keep (the oldest ones) */
1179          for (i = 0; i < nkeys - (nhist - 1); i++) {
1180              j = (i + nkeys + knext) % nkeys;
1181              histp = &adb->old_keys[j];
1182              for (j = 0; j < histp->n_key_data; j++) {
1183                  krb5_free_key_data_contents(context, &histp->key_data[j]);
1184              }
1185              free(histp->key_data);
1186          }
1187          free((void *)adb->old_keys);
1188          adb->old_keys = tmp;
1189          nkeys = adb->old_key_len = nhist - 1;
1190          knext = adb->old_key_next = 0;
1191      }
1192
1193      /*
1194       * If nhist decreased since the last password change, and nkeys+1
1195       * is less than the previous nhist, it is possible for knext to
1196       * index into unallocated space.  This condition would not be
1197       * caught by the resizing code above.
1198       */
1199      if (knext + 1 > nkeys)
1200          knext = adb->old_key_next = 0;
1201      /* free the old pw history entry if it contains data */
1202      histp = &adb->old_keys[knext];
1203      for (i = 0; i < histp->n_key_data; i++)
1204           krb5_free_key_data_contents(context, &histp->key_data[i]);
1205      free(histp->key_data);
1206
1207      /* store the new entry */
1208      adb->old_keys[knext] = *pw;
1209
1210      /* update the next pointer */
1211      if (++adb->old_key_next == nhist - 1)
1212          adb->old_key_next = 0;
1213
1214      return(0);
1215 }
1216
1217 /* FIXME: don't use global variable for this */
1218 krb5_boolean use_password_server = 0;
1219
1220 #ifdef USE_PASSWORD_SERVER
1221 static krb5_boolean
1222 kadm5_use_password_server (void)
1223 {
1224     return use_password_server;
1225 }
1226 #endif
1227
1228 void
1229 kadm5_set_use_password_server (void)
1230 {
1231     use_password_server = 1;
1232 }
1233
1234 #ifdef USE_PASSWORD_SERVER
1235
1236 /*
1237  * kadm5_launch_task () runs a program (task_path) to synchronize the
1238  * Apple password server with the Kerberos database.  Password server
1239  * programs can receive arguments on the command line (task_argv)
1240  * and a block of data via stdin (data_buffer).
1241  *
1242  * Because a failure to communicate with the tool results in the
1243  * password server falling out of sync with the database,
1244  * kadm5_launch_task() always fails if it can't talk to the tool.
1245  */
1246
1247 static kadm5_ret_t
1248 kadm5_launch_task (krb5_context context,
1249                    const char *task_path, char * const task_argv[],
1250                    const char *buffer)
1251 {
1252     kadm5_ret_t ret;
1253     int data_pipe[2];
1254
1255     ret = pipe (data_pipe);
1256     if (ret)
1257         ret = errno;
1258
1259     if (!ret) {
1260         pid_t pid = fork ();
1261         if (pid == -1) {
1262             ret = errno;
1263             close (data_pipe[0]);
1264             close (data_pipe[1]);
1265         } else if (pid == 0) {
1266             /* The child: */
1267
1268             if (dup2 (data_pipe[0], STDIN_FILENO) == -1)
1269                 _exit (1);
1270
1271             close (data_pipe[0]);
1272             close (data_pipe[1]);
1273
1274             execv (task_path, task_argv);
1275
1276             _exit (1); /* Fail if execv fails */
1277         } else {
1278             /* The parent: */
1279             int status;
1280
1281             ret = 0;
1282
1283             close (data_pipe[0]);
1284
1285             /* Write out the buffer to the child, add \n */
1286             if (buffer) {
1287                 if (krb5_net_write (context, data_pipe[1], buffer, strlen (buffer)) < 0
1288                     || krb5_net_write (context, data_pipe[1], "\n", 1) < 0)
1289                 {
1290                     /* kill the child to make sure waitpid() won't hang later */
1291                     ret = errno;
1292                     kill (pid, SIGKILL);
1293                 }
1294             }
1295             close (data_pipe[1]);
1296
1297             waitpid (pid, &status, 0);
1298
1299             if (!ret) {
1300                 if (WIFEXITED (status)) {
1301                     /* child read password and exited.  Check the return value. */
1302                     if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
1303                        ret = KRB5KDC_ERR_POLICY; /* password change rejected */
1304                     }
1305                 } else {
1306                     /* child read password but crashed or was killed */
1307                     ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1308                 }
1309             }
1310         }
1311     }
1312
1313     return ret;
1314 }
1315
1316 #endif
1317
1318 kadm5_ret_t
1319 kadm5_chpass_principal(void *server_handle,
1320                             krb5_principal principal, char *password)
1321 {
1322     return
1323         kadm5_chpass_principal_3(server_handle, principal, FALSE,
1324                                  0, NULL, password);
1325 }
1326
1327 kadm5_ret_t
1328 kadm5_chpass_principal_3(void *server_handle,
1329                          krb5_principal principal, krb5_boolean keepold,
1330                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1331                          char *password)
1332 {
1333     krb5_int32                  now;
1334     kadm5_policy_ent_rec        pol;
1335     osa_princ_ent_rec           adb;
1336     krb5_db_entry               kdb, kdb_save;
1337     int                         ret, ret2, last_pwd, hist_added;
1338     int                         have_pol = 0;
1339     kadm5_server_handle_t       handle = server_handle;
1340     osa_pw_hist_ent             hist;
1341     krb5_keyblock               *act_mkey;
1342     krb5_kvno                   act_kvno;
1343
1344     CHECK_HANDLE(server_handle);
1345
1346     krb5_clear_error_message(handle->context);
1347
1348     hist_added = 0;
1349     memset(&hist, 0, sizeof(hist));
1350
1351     if (principal == NULL || password == NULL)
1352         return EINVAL;
1353     if ((krb5_principal_compare(handle->context,
1354                                 principal, hist_princ)) == TRUE)
1355         return KADM5_PROTECT_PRINCIPAL;
1356
1357     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1358        return(ret);
1359
1360     /* we are going to need the current keys after the new keys are set */
1361     if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1362          kdb_free_entry(handle, &kdb, &adb);
1363          return(ret);
1364     }
1365
1366     if ((adb.aux_attributes & KADM5_POLICY)) {
1367         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1368              goto done;
1369         have_pol = 1;
1370     }
1371
1372     if ((ret = passwd_check(handle, password, adb.aux_attributes &
1373                             KADM5_POLICY, &pol, principal)))
1374          goto done;
1375
1376     ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1377                                  active_mkey_list, &act_kvno, &act_mkey);
1378     if (ret)
1379         goto done;
1380
1381     ret = krb5_dbe_cpw(handle->context, act_mkey,
1382                        n_ks_tuple?ks_tuple:handle->params.keysalts,
1383                        n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1384                        password, 0 /* increment kvno */,
1385                        keepold, &kdb);
1386     if (ret)
1387         goto done;
1388
1389     ret = krb5_dbe_update_mkvno(handle->context, &kdb, act_kvno);
1390     if (ret)
1391          goto done;
1392
1393     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1394
1395     ret = krb5_timeofday(handle->context, &now);
1396     if (ret)
1397          goto done;
1398
1399     if ((adb.aux_attributes & KADM5_POLICY)) {
1400        /* the policy was loaded before */
1401
1402         ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1403                                               &kdb, &last_pwd);
1404         if (ret)
1405             goto done;
1406
1407 #if 0
1408          /*
1409           * The spec says this check is overridden if the caller has
1410           * modify privilege.  The admin server therefore makes this
1411           * check itself (in chpass_principal_wrapper, misc.c). A
1412           * local caller implicitly has all authorization bits.
1413           */
1414         if ((now - last_pwd) < pol.pw_min_life &&
1415             !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1416              ret = KADM5_PASS_TOOSOON;
1417              goto done;
1418         }
1419 #endif
1420
1421         ret = create_history_entry(handle->context,
1422                                    act_mkey,
1423                                    kdb_save.n_key_data,
1424                                    kdb_save.key_data, &hist);
1425         if (ret)
1426             goto done;
1427
1428         ret = check_pw_reuse(handle->context, act_mkey, &hist_key,
1429                              kdb.n_key_data, kdb.key_data,
1430                              1, &hist);
1431         if (ret)
1432             goto done;
1433
1434         if (pol.pw_history_num > 1) {
1435             if (adb.admin_history_kvno != hist_kvno) {
1436                 ret = KADM5_BAD_HIST_KEY;
1437                 goto done;
1438             }
1439
1440             ret = check_pw_reuse(handle->context, act_mkey, &hist_key,
1441                                  kdb.n_key_data, kdb.key_data,
1442                                  adb.old_key_len, adb.old_keys);
1443             if (ret)
1444                 goto done;
1445
1446             ret = add_to_history(handle->context, &adb, &pol, &hist);
1447             if (ret)
1448                 goto done;
1449             hist_added = 1;
1450        }
1451
1452         if (pol.pw_max_life)
1453            kdb.pw_expiration = now + pol.pw_max_life;
1454         else
1455            kdb.pw_expiration = 0;
1456     } else {
1457         kdb.pw_expiration = 0;
1458     }
1459
1460 #ifdef USE_PASSWORD_SERVER
1461     if (kadm5_use_password_server () &&
1462         (krb5_princ_size (handle->context, principal) == 1)) {
1463         krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
1464         const char *path = "/usr/sbin/mkpassdb";
1465         char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
1466         char *pstring = NULL;
1467
1468         if (!ret) {
1469             pstring = malloc ((princ->length + 1) * sizeof (char));
1470             if (pstring == NULL) { ret = ENOMEM; }
1471         }
1472
1473         if (!ret) {
1474             memcpy (pstring, princ->data, princ->length);
1475             pstring [princ->length] = '\0';
1476             argv[2] = pstring;
1477
1478             ret = kadm5_launch_task (handle->context, path, argv, password);
1479         }
1480
1481         if (pstring != NULL)
1482             free (pstring);
1483
1484         if (ret)
1485             goto done;
1486     }
1487 #endif
1488
1489     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1490     if (ret)
1491         goto done;
1492
1493     /* key data and attributes changed, let the database provider know */
1494     kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES /* | KADM5_CPW_FUNCTION */;
1495
1496     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1497         goto done;
1498
1499     ret = KADM5_OK;
1500 done:
1501     if (!hist_added && hist.key_data)
1502          free_history_entry(handle->context, &hist);
1503     kdb_free_entry(handle, &kdb, &adb);
1504     kdb_free_entry(handle, &kdb_save, NULL);
1505     krb5_db_free_principal(handle->context, &kdb, 1);
1506
1507     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1508         && !ret)
1509          ret = ret2;
1510
1511     return ret;
1512 }
1513
1514 kadm5_ret_t
1515 kadm5_randkey_principal(void *server_handle,
1516                         krb5_principal principal,
1517                         krb5_keyblock **keyblocks,
1518                         int *n_keys)
1519 {
1520     return
1521         kadm5_randkey_principal_3(server_handle, principal,
1522                                   FALSE, 0, NULL,
1523                                   keyblocks, n_keys);
1524 }
1525 kadm5_ret_t
1526 kadm5_randkey_principal_3(void *server_handle,
1527                         krb5_principal principal,
1528                         krb5_boolean keepold,
1529                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1530                         krb5_keyblock **keyblocks,
1531                         int *n_keys)
1532 {
1533     krb5_db_entry               kdb;
1534     osa_princ_ent_rec           adb;
1535     krb5_int32                  now;
1536     kadm5_policy_ent_rec        pol;
1537     krb5_key_data               *key_data;
1538     int                         ret, last_pwd, have_pol = 0;
1539     kadm5_server_handle_t       handle = server_handle;
1540     krb5_keyblock               *act_mkey;
1541
1542     if (keyblocks)
1543          *keyblocks = NULL;
1544
1545     CHECK_HANDLE(server_handle);
1546
1547     krb5_clear_error_message(handle->context);
1548
1549     if (principal == NULL)
1550         return EINVAL;
1551     if (hist_princ && /* this will be NULL when initializing the databse */
1552         ((krb5_principal_compare(handle->context,
1553                                  principal, hist_princ)) == TRUE))
1554         return KADM5_PROTECT_PRINCIPAL;
1555
1556     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1557        return(ret);
1558
1559     ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1560                                  active_mkey_list, NULL, &act_mkey);
1561     if (ret)
1562         goto done;
1563
1564     ret = krb5_dbe_crk(handle->context, act_mkey,
1565                        n_ks_tuple?ks_tuple:handle->params.keysalts,
1566                        n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1567                        keepold,
1568                        &kdb);
1569     if (ret)
1570         goto done;
1571
1572     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1573
1574     ret = krb5_timeofday(handle->context, &now);
1575     if (ret)
1576         goto done;
1577
1578     if ((adb.aux_attributes & KADM5_POLICY)) {
1579         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1580                                     &pol)) != KADM5_OK)
1581            goto done;
1582         have_pol = 1;
1583
1584         ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1585                                               &kdb, &last_pwd);
1586         if (ret)
1587              goto done;
1588
1589 #if 0
1590          /*
1591           * The spec says this check is overridden if the caller has
1592           * modify privilege.  The admin server therefore makes this
1593           * check itself (in chpass_principal_wrapper, misc.c).  A
1594           * local caller implicitly has all authorization bits.
1595           */
1596         if((now - last_pwd) < pol.pw_min_life &&
1597            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1598              ret = KADM5_PASS_TOOSOON;
1599              goto done;
1600         }
1601 #endif
1602
1603         if(pol.pw_history_num > 1) {
1604             if(adb.admin_history_kvno != hist_kvno) {
1605                 ret = KADM5_BAD_HIST_KEY;
1606                 goto done;
1607             }
1608
1609             ret = check_pw_reuse(handle->context, act_mkey, &hist_key,
1610                                  kdb.n_key_data, kdb.key_data,
1611                                  adb.old_key_len, adb.old_keys);
1612             if (ret)
1613                 goto done;
1614         }
1615         if (pol.pw_max_life)
1616            kdb.pw_expiration = now + pol.pw_max_life;
1617         else
1618            kdb.pw_expiration = 0;
1619     } else {
1620         kdb.pw_expiration = 0;
1621     }
1622
1623     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1624     if (ret)
1625          goto done;
1626
1627     if (keyblocks) {
1628          if (handle->api_version == KADM5_API_VERSION_1) {
1629               /* Version 1 clients will expect to see a DES_CRC enctype. */
1630              ret = krb5_dbe_find_enctype(handle->context, &kdb,
1631                                          ENCTYPE_DES_CBC_CRC,
1632                                          -1, -1, &key_data);
1633              if (ret)
1634                  goto done;
1635
1636              ret = decrypt_key_data(handle->context, act_mkey, 1, key_data,
1637                                      keyblocks, NULL);
1638              if (ret)
1639                  goto done;
1640          } else {
1641              ret = decrypt_key_data(handle->context, act_mkey,
1642                                      kdb.n_key_data, kdb.key_data,
1643                                      keyblocks, n_keys);
1644              if (ret)
1645                  goto done;
1646          }
1647     }
1648
1649     /* key data changed, let the database provider know */
1650     kdb.mask = KADM5_KEY_DATA /* | KADM5_RANDKEY_USED */;
1651
1652     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1653         goto done;
1654
1655     ret = KADM5_OK;
1656 done:
1657     kdb_free_entry(handle, &kdb, &adb);
1658     if (have_pol)
1659          kadm5_free_policy_ent(handle->lhandle, &pol);
1660
1661     return ret;
1662 }
1663
1664 /*
1665  * kadm5_setv4key_principal:
1666  *
1667  * Set only ONE key of the principal, removing all others.  This key
1668  * must have the DES_CBC_CRC enctype and is entered as having the
1669  * krb4 salttype.  This is to enable things like kadmind4 to work.
1670  */
1671 kadm5_ret_t
1672 kadm5_setv4key_principal(void *server_handle,
1673                        krb5_principal principal,
1674                        krb5_keyblock *keyblock)
1675 {
1676     krb5_db_entry               kdb;
1677     osa_princ_ent_rec           adb;
1678     krb5_int32                  now;
1679     kadm5_policy_ent_rec        pol;
1680     krb5_keysalt                keysalt;
1681     int                         i, k, kvno, ret, have_pol = 0;
1682 #if 0
1683     int                         last_pwd;
1684 #endif
1685     kadm5_server_handle_t       handle = server_handle;
1686     krb5_key_data               tmp_key_data;
1687     krb5_keyblock               *act_mkey;
1688
1689     memset( &tmp_key_data, 0, sizeof(tmp_key_data));
1690
1691     CHECK_HANDLE(server_handle);
1692
1693     krb5_clear_error_message(handle->context);
1694
1695     if (principal == NULL || keyblock == NULL)
1696         return EINVAL;
1697     if (hist_princ && /* this will be NULL when initializing the databse */
1698         ((krb5_principal_compare(handle->context,
1699                                  principal, hist_princ)) == TRUE))
1700         return KADM5_PROTECT_PRINCIPAL;
1701
1702     if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1703         return KADM5_SETV4KEY_INVAL_ENCTYPE;
1704
1705     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1706        return(ret);
1707
1708     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1709          if (kdb.key_data[i].key_data_kvno > kvno)
1710               kvno = kdb.key_data[i].key_data_kvno;
1711
1712     if (kdb.key_data != NULL)
1713          cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1714
1715     kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
1716     if (kdb.key_data == NULL)
1717          return ENOMEM;
1718     memset(kdb.key_data, 0, sizeof(krb5_key_data));
1719     kdb.n_key_data = 1;
1720     keysalt.type = KRB5_KDB_SALTTYPE_V4;
1721     /* XXX data.magic? */
1722     keysalt.data.length = 0;
1723     keysalt.data.data = NULL;
1724
1725     ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1726                                  active_mkey_list, NULL, &act_mkey);
1727     if (ret)
1728         goto done;
1729
1730     /* use tmp_key_data as temporary location and reallocate later */
1731     ret = krb5_dbekd_encrypt_key_data(handle->context, act_mkey,
1732                                       keyblock, &keysalt, kvno + 1,
1733                                       &tmp_key_data);
1734     if (ret) {
1735         goto done;
1736     }
1737
1738     for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1739         kdb.key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
1740         kdb.key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
1741         if (tmp_key_data.key_data_contents[k]) {
1742             kdb.key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1743             if (kdb.key_data->key_data_contents[k] == NULL) {
1744                 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1745                 kdb.key_data = NULL;
1746                 kdb.n_key_data = 0;
1747                 ret = ENOMEM;
1748                 goto done;
1749             }
1750             memcpy (kdb.key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1751
1752             memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1753             free (tmp_key_data.key_data_contents[k]);
1754             tmp_key_data.key_data_contents[k] = NULL;
1755         }
1756     }
1757
1758
1759
1760     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1761
1762     ret = krb5_timeofday(handle->context, &now);
1763     if (ret)
1764         goto done;
1765
1766     if ((adb.aux_attributes & KADM5_POLICY)) {
1767         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1768                                     &pol)) != KADM5_OK)
1769            goto done;
1770         have_pol = 1;
1771
1772 #if 0
1773         /*
1774           * The spec says this check is overridden if the caller has
1775           * modify privilege.  The admin server therefore makes this
1776           * check itself (in chpass_principal_wrapper, misc.c).  A
1777           * local caller implicitly has all authorization bits.
1778           */
1779         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1780                                                   &kdb, &last_pwd))
1781              goto done;
1782         if((now - last_pwd) < pol.pw_min_life &&
1783            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1784              ret = KADM5_PASS_TOOSOON;
1785              goto done;
1786         }
1787 #endif
1788 #if 0
1789         /*
1790          * Should we be checking/updating pw history here?
1791          */
1792         if(pol.pw_history_num > 1) {
1793             if(adb.admin_history_kvno != hist_kvno) {
1794                 ret = KADM5_BAD_HIST_KEY;
1795                 goto done;
1796             }
1797
1798             if (ret = check_pw_reuse(handle->context,
1799                                      &hist_key,
1800                                      kdb.n_key_data, kdb.key_data,
1801                                      adb.old_key_len, adb.old_keys))
1802                 goto done;
1803         }
1804 #endif
1805
1806         if (pol.pw_max_life)
1807            kdb.pw_expiration = now + pol.pw_max_life;
1808         else
1809            kdb.pw_expiration = 0;
1810     } else {
1811         kdb.pw_expiration = 0;
1812     }
1813
1814     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1815     if (ret)
1816          goto done;
1817
1818     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1819         goto done;
1820
1821     ret = KADM5_OK;
1822 done:
1823     for (i = 0; i < tmp_key_data.key_data_ver; i++) {
1824         if (tmp_key_data.key_data_contents[i]) {
1825             memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
1826             free (tmp_key_data.key_data_contents[i]);
1827         }
1828     }
1829
1830     kdb_free_entry(handle, &kdb, &adb);
1831     if (have_pol)
1832          kadm5_free_policy_ent(handle->lhandle, &pol);
1833
1834     return ret;
1835 }
1836
1837 kadm5_ret_t
1838 kadm5_setkey_principal(void *server_handle,
1839                        krb5_principal principal,
1840                        krb5_keyblock *keyblocks,
1841                        int n_keys)
1842 {
1843     return
1844         kadm5_setkey_principal_3(server_handle, principal,
1845                                  FALSE, 0, NULL,
1846                                  keyblocks, n_keys);
1847 }
1848
1849 kadm5_ret_t
1850 kadm5_setkey_principal_3(void *server_handle,
1851                          krb5_principal principal,
1852                          krb5_boolean keepold,
1853                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1854                          krb5_keyblock *keyblocks,
1855                          int n_keys)
1856 {
1857     krb5_db_entry               kdb;
1858     osa_princ_ent_rec           adb;
1859     krb5_int32                  now;
1860     kadm5_policy_ent_rec        pol;
1861     krb5_key_data               *old_key_data;
1862     int                         n_old_keys;
1863     int                         i, j, k, kvno, ret, have_pol = 0;
1864 #if 0
1865     int                         last_pwd;
1866 #endif
1867     kadm5_server_handle_t       handle = server_handle;
1868     krb5_boolean                similar;
1869     krb5_keysalt                keysalt;
1870     krb5_key_data         tmp_key_data;
1871     krb5_key_data        *tptr;
1872     krb5_keyblock               *act_mkey;
1873
1874     CHECK_HANDLE(server_handle);
1875
1876     krb5_clear_error_message(handle->context);
1877
1878     if (principal == NULL || keyblocks == NULL)
1879         return EINVAL;
1880     if (hist_princ && /* this will be NULL when initializing the databse */
1881         ((krb5_principal_compare(handle->context,
1882                                  principal, hist_princ)) == TRUE))
1883         return KADM5_PROTECT_PRINCIPAL;
1884
1885     for (i = 0; i < n_keys; i++) {
1886         for (j = i+1; j < n_keys; j++) {
1887             if ((ret = krb5_c_enctype_compare(handle->context,
1888                                               keyblocks[i].enctype,
1889                                               keyblocks[j].enctype,
1890                                               &similar)))
1891                 return(ret);
1892             if (similar) {
1893                 if (n_ks_tuple) {
1894                     if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1895                         return KADM5_SETKEY_DUP_ENCTYPES;
1896                 } else
1897                     return KADM5_SETKEY_DUP_ENCTYPES;
1898             }
1899         }
1900     }
1901
1902     if (n_ks_tuple && n_ks_tuple != n_keys)
1903         return KADM5_SETKEY3_ETYPE_MISMATCH;
1904
1905     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1906        return(ret);
1907
1908     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1909          if (kdb.key_data[i].key_data_kvno > kvno)
1910               kvno = kdb.key_data[i].key_data_kvno;
1911
1912     if (keepold) {
1913         old_key_data = kdb.key_data;
1914         n_old_keys = kdb.n_key_data;
1915     } else {
1916         if (kdb.key_data != NULL)
1917             cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1918         n_old_keys = 0;
1919         old_key_data = NULL;
1920     }
1921
1922     kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
1923                                                  *sizeof(krb5_key_data));
1924     if (kdb.key_data == NULL) {
1925         ret = ENOMEM;
1926         goto done;
1927     }
1928
1929     memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1930     kdb.n_key_data = 0;
1931
1932     for (i = 0; i < n_keys; i++) {
1933         if (n_ks_tuple) {
1934             keysalt.type = ks_tuple[i].ks_salttype;
1935             keysalt.data.length = 0;
1936             keysalt.data.data = NULL;
1937             if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1938                 ret = KADM5_SETKEY3_ETYPE_MISMATCH;
1939                 goto done;
1940             }
1941         }
1942         memset (&tmp_key_data, 0, sizeof(tmp_key_data));
1943
1944         ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1945                                      active_mkey_list, NULL, &act_mkey);
1946         if (ret)
1947             goto done;
1948
1949         ret = krb5_dbekd_encrypt_key_data(handle->context,
1950                                           act_mkey,
1951                                           &keyblocks[i],
1952                                           n_ks_tuple ? &keysalt : NULL,
1953                                           kvno + 1,
1954                                           &tmp_key_data);
1955         if (ret)
1956             goto done;
1957
1958         tptr = &kdb.key_data[i];
1959         tptr->key_data_ver = tmp_key_data.key_data_ver;
1960         tptr->key_data_kvno = tmp_key_data.key_data_kvno;
1961         for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1962             tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
1963             tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
1964             if (tmp_key_data.key_data_contents[k]) {
1965                 tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1966                 if (tptr->key_data_contents[k] == NULL) {
1967                     int i1;
1968                     for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
1969                         if (tmp_key_data.key_data_contents[i1]) {
1970                             memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
1971                             free (tmp_key_data.key_data_contents[i1]);
1972                         }
1973                     }
1974
1975                     ret =  ENOMEM;
1976                     goto done;
1977                 }
1978                 memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1979
1980                 memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1981                 free (tmp_key_data.key_data_contents[k]);
1982                 tmp_key_data.key_data_contents[k] = NULL;
1983             }
1984         }
1985         kdb.n_key_data++;
1986     }
1987
1988     /* copy old key data if necessary */
1989     for (i = 0; i < n_old_keys; i++) {
1990         kdb.key_data[i+n_keys] = old_key_data[i];
1991         memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1992         kdb.n_key_data++;
1993     }
1994
1995     if (old_key_data)
1996         krb5_db_free(handle->context, old_key_data);
1997
1998     /* assert(kdb.n_key_data == n_keys + n_old_keys) */
1999     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
2000
2001     if ((ret = krb5_timeofday(handle->context, &now)))
2002         goto done;
2003
2004     if ((adb.aux_attributes & KADM5_POLICY)) {
2005         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
2006                                     &pol)) != KADM5_OK)
2007            goto done;
2008         have_pol = 1;
2009
2010 #if 0
2011         /*
2012           * The spec says this check is overridden if the caller has
2013           * modify privilege.  The admin server therefore makes this
2014           * check itself (in chpass_principal_wrapper, misc.c).  A
2015           * local caller implicitly has all authorization bits.
2016           */
2017         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
2018                                                   &kdb, &last_pwd))
2019              goto done;
2020         if((now - last_pwd) < pol.pw_min_life &&
2021            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2022              ret = KADM5_PASS_TOOSOON;
2023              goto done;
2024         }
2025 #endif
2026 #if 0
2027         /*
2028          * Should we be checking/updating pw history here?
2029          */
2030         if (pol.pw_history_num > 1) {
2031             if(adb.admin_history_kvno != hist_kvno) {
2032                 ret = KADM5_BAD_HIST_KEY;
2033                 goto done;
2034             }
2035
2036             if (ret = check_pw_reuse(handle->context,
2037                                      &hist_key,
2038                                      kdb.n_key_data, kdb.key_data,
2039                                      adb.old_key_len, adb.old_keys))
2040                 goto done;
2041         }
2042 #endif
2043
2044         if (pol.pw_max_life)
2045            kdb.pw_expiration = now + pol.pw_max_life;
2046         else
2047            kdb.pw_expiration = 0;
2048     } else {
2049         kdb.pw_expiration = 0;
2050     }
2051
2052     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
2053         goto done;
2054
2055     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
2056         goto done;
2057
2058     ret = KADM5_OK;
2059 done:
2060     kdb_free_entry(handle, &kdb, &adb);
2061     if (have_pol)
2062          kadm5_free_policy_ent(handle->lhandle, &pol);
2063
2064     return ret;
2065 }
2066
2067 /*
2068  * Return the list of keys like kadm5_randkey_principal,
2069  * but don't modify the principal.
2070  */
2071 kadm5_ret_t
2072 kadm5_get_principal_keys(void *server_handle /* IN */,
2073                          krb5_principal principal /* IN */,
2074                          krb5_keyblock **keyblocks /* OUT */,
2075                          int *n_keys /* OUT */)
2076 {
2077     krb5_db_entry               kdb;
2078     osa_princ_ent_rec           adb;
2079     krb5_key_data               *key_data;
2080     kadm5_ret_t                 ret;
2081     kadm5_server_handle_t       handle = server_handle;
2082     krb5_keyblock               *mkey_ptr;
2083
2084     if (keyblocks)
2085          *keyblocks = NULL;
2086
2087     CHECK_HANDLE(server_handle);
2088
2089     if (principal == NULL)
2090         return EINVAL;
2091
2092     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
2093        return(ret);
2094
2095     if (keyblocks) {
2096         if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &kdb,
2097                                       &mkey_ptr))) {
2098             krb5_keylist_node *tmp_mkey_list;
2099             /* try refreshing master key list */
2100             /* XXX it would nice if we had the mkvno here for optimization */
2101             if (krb5_db_fetch_mkey_list(handle->context, master_princ,
2102                                         &master_keyblock, 0,
2103                                         &tmp_mkey_list) == 0) {
2104                 krb5_dbe_free_key_list(handle->context, master_keylist);
2105                 master_keylist = tmp_mkey_list;
2106                 if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
2107                                               &kdb, &mkey_ptr))) {
2108                     goto done;
2109                 }
2110             } else {
2111                 goto done;
2112             }
2113         }
2114
2115          if (handle->api_version == KADM5_API_VERSION_1) {
2116               /* Version 1 clients will expect to see a DES_CRC enctype. */
2117               if ((ret = krb5_dbe_find_enctype(handle->context, &kdb,
2118                                               ENCTYPE_DES_CBC_CRC,
2119                                               -1, -1, &key_data)))
2120                    goto done;
2121
2122               if ((ret = decrypt_key_data(handle->context, mkey_ptr, 1, key_data,
2123                                          keyblocks, NULL)))
2124                    goto done;
2125          } else {
2126               ret = decrypt_key_data(handle->context, mkey_ptr,
2127                                      kdb.n_key_data, kdb.key_data,
2128                                      keyblocks, n_keys);
2129               if (ret)
2130                    goto done;
2131          }
2132     }
2133
2134     ret = KADM5_OK;
2135 done:
2136     kdb_free_entry(handle, &kdb, &adb);
2137
2138     return ret;
2139 }
2140
2141
2142 /*
2143  * Allocate an array of n_key_data krb5_keyblocks, fill in each
2144  * element with the results of decrypting the nth key in key_data with
2145  * mkey, and if n_keys is not NULL fill it in with the
2146  * number of keys decrypted.
2147  */
2148 static int decrypt_key_data(krb5_context context, krb5_keyblock *mkey,
2149                             int n_key_data, krb5_key_data *key_data,
2150                             krb5_keyblock **keyblocks, int *n_keys)
2151 {
2152      krb5_keyblock *keys;
2153      int ret, i;
2154
2155      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
2156      if (keys == NULL)
2157           return ENOMEM;
2158      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
2159
2160      for (i = 0; i < n_key_data; i++) {
2161           ret = krb5_dbekd_decrypt_key_data(context, mkey,
2162                                             &key_data[i],
2163                                             &keys[i], NULL);
2164           if (ret) {
2165                for (; i >= 0; i--) {
2166                    if (keys[i].contents) {
2167                        memset (keys[i].contents, 0, keys[i].length);
2168                        free( keys[i].contents );
2169                    }
2170                }
2171
2172                memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
2173                free(keys);
2174                return ret;
2175           }
2176      }
2177
2178      *keyblocks = keys;
2179      if (n_keys)
2180           *n_keys = n_key_data;
2181
2182      return 0;
2183 }
2184
2185 /*
2186  * Function: kadm5_decrypt_key
2187  *
2188  * Purpose: Retrieves and decrypts a principal key.
2189  *
2190  * Arguments:
2191  *
2192  *      server_handle   (r) kadm5 handle
2193  *      entry           (r) principal retrieved with kadm5_get_principal
2194  *      ktype           (r) enctype to search for, or -1 to ignore
2195  *      stype           (r) salt type to search for, or -1 to ignore
2196  *      kvno            (r) kvno to search for, -1 for max, 0 for max
2197  *                      only if it also matches ktype and stype
2198  *      keyblock        (w) keyblock to fill in
2199  *      keysalt         (w) keysalt to fill in, or NULL
2200  *      kvnop           (w) kvno to fill in, or NULL
2201  *
2202  * Effects: Searches the key_data array of entry, which must have been
2203  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2204  * find a key with a specified enctype, salt type, and kvno in a
2205  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
2206  * it with the master key, and return the key in keyblock, the salt
2207  * in salttype, and the key version number in kvno.
2208  *
2209  * If ktype or stype is -1, it is ignored for the search.  If kvno is
2210  * -1, ktype and stype are ignored and the key with the max kvno is
2211  * returned.  If kvno is 0, only the key with the max kvno is returned
2212  * and only if it matches the ktype and stype; otherwise, ENOENT is
2213  * returned.
2214  */
2215 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
2216                               kadm5_principal_ent_t entry, krb5_int32
2217                               ktype, krb5_int32 stype, krb5_int32
2218                               kvno, krb5_keyblock *keyblock,
2219                               krb5_keysalt *keysalt, int *kvnop)
2220 {
2221     kadm5_server_handle_t handle = server_handle;
2222     krb5_db_entry dbent;
2223     krb5_key_data *key_data;
2224     krb5_keyblock *mkey_ptr;
2225     int ret;
2226
2227     CHECK_HANDLE(server_handle);
2228
2229     if (entry->n_key_data == 0 || entry->key_data == NULL)
2230          return EINVAL;
2231
2232     /* find_enctype only uses these two fields */
2233     dbent.n_key_data = entry->n_key_data;
2234     dbent.key_data = entry->key_data;
2235     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
2236                                     stype, kvno, &key_data)))
2237          return ret;
2238
2239     /* find_mkey only uses this field */
2240     dbent.tl_data = entry->tl_data;
2241     if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent,
2242                                   &mkey_ptr))) {
2243         krb5_keylist_node *tmp_mkey_list;
2244         /* try refreshing master key list */
2245         /* XXX it would nice if we had the mkvno here for optimization */
2246         if (krb5_db_fetch_mkey_list(handle->context, master_princ,
2247                                     &master_keyblock, 0, &tmp_mkey_list) == 0) {
2248             krb5_dbe_free_key_list(handle->context, master_keylist);
2249             master_keylist = tmp_mkey_list;
2250             if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
2251                                           &dbent, &mkey_ptr))) {
2252                 return ret;
2253             }
2254         } else {
2255             return ret;
2256         }
2257     }
2258
2259     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
2260                                            mkey_ptr, key_data,
2261                                            keyblock, keysalt)))
2262          return ret;
2263
2264     /*
2265      * Coerce the enctype of the output keyblock in case we got an
2266      * inexact match on the enctype; this behavior will go away when
2267      * the key storage architecture gets redesigned for 1.3.
2268      */
2269     if (ktype != -1)
2270         keyblock->enctype = ktype;
2271
2272     if (kvnop)
2273          *kvnop = key_data->key_data_kvno;
2274
2275     return KADM5_OK;
2276 }