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