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