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