Thanks, patch applied
[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     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
414     if (ret)
415         return(ret);
416
417     /*
418      * This is pretty much the same as create ...
419      */
420
421     if ((mask & KADM5_POLICY)) {
422          /* get the new policy */
423          ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
424          if (ret) {
425               switch (ret) {
426               case EINVAL:
427                    ret = KADM5_BAD_POLICY;
428                    break;
429               case KADM5_UNK_POLICY:
430               case KADM5_BAD_POLICY:
431                    ret =  KADM5_UNK_POLICY;
432                    break;
433               }
434               goto done;
435          }
436          have_npol = 1;
437
438          /* if we already have a policy, get it to decrement the refcnt */
439          if(adb.aux_attributes & KADM5_POLICY) {
440               /* ... but not if the old and new are the same */
441               if(strcmp(adb.policy, entry->policy)) {
442                    ret = kadm5_get_policy(handle->lhandle,
443                                           adb.policy, &opol);
444                    switch(ret) {
445                    case EINVAL:
446                    case KADM5_BAD_POLICY:
447                    case KADM5_UNK_POLICY:
448                         break;
449                    case KADM5_OK:
450                         have_opol = 1;
451                         opol.policy_refcnt--;
452                         break;
453                    default:
454                         goto done;
455                         break;
456                    }
457                    npol.policy_refcnt++;
458               }
459          } else npol.policy_refcnt++;
460
461          /* set us up to use the new policy */
462          adb.aux_attributes |= KADM5_POLICY;
463          if (adb.policy)
464               free(adb.policy);
465          adb.policy = strdup(entry->policy);
466
467          /* set pw_max_life based on new policy */
468          if (npol.pw_max_life) {
469              ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
470                                                    &(kdb.pw_expiration));
471              if (ret)
472                  goto done;
473              kdb.pw_expiration += npol.pw_max_life;
474          } else {
475              kdb.pw_expiration = 0;
476          }
477     }
478
479     if ((mask & KADM5_POLICY_CLR) &&
480         (adb.aux_attributes & KADM5_POLICY)) {
481          ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
482          switch(ret) {
483          case EINVAL:
484          case KADM5_BAD_POLICY:
485          case KADM5_UNK_POLICY:
486               ret = KADM5_BAD_DB;
487               goto done;
488               break;
489          case KADM5_OK:
490               have_opol = 1;
491               if (adb.policy)
492                    free(adb.policy);
493               adb.policy = NULL;
494               adb.aux_attributes &= ~KADM5_POLICY;
495               kdb.pw_expiration = 0;
496               opol.policy_refcnt--;
497               break;
498          default:
499               goto done;
500               break;
501          }
502     }
503
504     if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
505         (((have_opol) &&
506           (ret =
507            kadm5_modify_policy_internal(handle->lhandle, &opol,
508                                              KADM5_REF_COUNT))) ||
509          ((have_npol) &&
510           (ret =
511            kadm5_modify_policy_internal(handle->lhandle, &npol,
512                                              KADM5_REF_COUNT)))))
513         goto done;
514
515     if ((mask & KADM5_ATTRIBUTES)) 
516         kdb.attributes = entry->attributes;
517     if ((mask & KADM5_MAX_LIFE))
518         kdb.max_life = entry->max_life;
519     if ((mask & KADM5_PRINC_EXPIRE_TIME))
520         kdb.expiration = entry->princ_expire_time;
521     if (mask & KADM5_PW_EXPIRATION)
522          kdb.pw_expiration = entry->pw_expiration;
523     if (mask & KADM5_MAX_RLIFE)
524          kdb.max_renewable_life = entry->max_renewable_life;
525     if (mask & KADM5_FAIL_AUTH_COUNT)
526          kdb.fail_auth_count = entry->fail_auth_count;
527     
528     if((mask & KADM5_KVNO)) {
529          for (i = 0; i < kdb.n_key_data; i++)
530               kdb.key_data[i].key_data_kvno = entry->kvno;
531     }
532
533     if (mask & KADM5_TL_DATA) {
534          krb5_tl_data *tl, *tl2;
535          /*
536           * Replace kdb.tl_data with what was passed in.  The
537           * KRB5_TL_KADM_DATA will be re-added (based on adb) by
538           * kdb_put_entry, below.
539           *
540           * Note that we have to duplicate the passed in tl_data
541           * before adding it to kdb.  The reason is that kdb_put_entry
542           * will add its own tl_data entries that we will need to
543           * free, but we cannot free the caller's tl_data (an
544           * alternative would be to scan the tl_data after put_entry
545           * and only free those entries that were not passed in).
546           */
547          while (kdb.tl_data) {
548               tl = kdb.tl_data->tl_data_next;
549               free(kdb.tl_data->tl_data_contents);
550               free(kdb.tl_data);
551               kdb.tl_data = tl;
552          }
553
554          kdb.n_tl_data = entry->n_tl_data;
555          kdb.tl_data = NULL;
556          tl2 = entry->tl_data;
557          while (tl2) {
558               tl = dup_tl_data(tl2);
559               tl->tl_data_next = kdb.tl_data;
560               kdb.tl_data = tl;
561               tl2 = tl2->tl_data_next;
562          }
563     }
564
565     ret = kdb_put_entry(handle, &kdb, &adb);
566     if (ret) goto done;
567
568     ret = KADM5_OK;
569 done:
570     if (have_opol) {
571          ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
572          ret = ret ? ret : ret2;
573     }
574     if (have_npol) {
575          ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
576          ret = ret ? ret : ret2;
577     }
578     kdb_free_entry(handle, &kdb, &adb);
579     return ret;
580 }
581     
582 kadm5_ret_t
583 kadm5_rename_principal(void *server_handle,
584                             krb5_principal source, krb5_principal target)
585 {
586     krb5_db_entry       kdb;
587     osa_princ_ent_rec   adb;
588     int                 ret, i;
589     kadm5_server_handle_t handle = server_handle;
590
591     CHECK_HANDLE(server_handle);
592
593     if (source == NULL || target == NULL)
594         return EINVAL;
595
596     if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
597         kdb_free_entry(handle, &kdb, &adb);
598         return(KADM5_DUP);
599     }
600
601     if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
602         return ret;
603
604     /* this is kinda gross, but unavoidable */
605
606     for (i=0; i<kdb.n_key_data; i++) {
607         if ((kdb.key_data[i].key_data_ver == 1) ||
608             (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
609             ret = KADM5_NO_RENAME_SALT;
610             goto done;
611         }
612     }
613
614     krb5_free_principal(handle->context, kdb.princ);
615     ret = krb5_copy_principal(handle->context, target, &kdb.princ);
616     if (ret) {
617         kdb.princ = NULL; /* so freeing the dbe doesn't lose */
618         goto done;
619     }
620
621     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
622         goto done;
623
624     ret = kdb_delete_entry(handle, source);
625
626 done:
627     kdb_free_entry(handle, &kdb, &adb);
628     return ret;
629 }
630
631 kadm5_ret_t
632 kadm5_get_principal(void *server_handle, krb5_principal principal,
633                     kadm5_principal_ent_t entry,
634                     long in_mask)
635 {
636     krb5_db_entry               kdb;
637     osa_princ_ent_rec           adb;
638     osa_adb_ret_t               ret = 0;
639     long                        mask;
640     int i;
641     kadm5_server_handle_t handle = server_handle;
642     kadm5_principal_ent_rec     entry_local, *entry_orig;
643
644     CHECK_HANDLE(server_handle);
645
646     /*
647      * In version 1, all the defined fields are always returned.
648      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
649      * filled with allocated memory.
650      */
651     if (handle->api_version == KADM5_API_VERSION_1) {
652          mask = KADM5_PRINCIPAL_NORMAL_MASK;
653          entry_orig = entry;
654          entry = &entry_local;
655     } else {
656          mask = in_mask;
657     }
658
659     memset((char *) entry, 0, sizeof(*entry));
660
661     if (principal == NULL)
662         return EINVAL;
663
664     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
665         return ret;
666
667     if ((mask & KADM5_POLICY) &&
668         adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
669         if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
670             ret = ENOMEM;
671             goto done;
672         }
673         strcpy(entry->policy, adb.policy);
674     }
675
676     if (mask & KADM5_AUX_ATTRIBUTES)
677          entry->aux_attributes = adb.aux_attributes;
678
679     if ((mask & KADM5_PRINCIPAL) &&
680         (ret = krb5_copy_principal(handle->context, principal,
681                                    &entry->principal))) { 
682         goto done;
683     }
684
685     if (mask & KADM5_PRINC_EXPIRE_TIME)
686          entry->princ_expire_time = kdb.expiration;
687
688     if ((mask & KADM5_LAST_PWD_CHANGE) &&
689         (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
690                                                &(entry->last_pwd_change)))) {
691         goto done;
692     }
693
694     if (mask & KADM5_PW_EXPIRATION)
695          entry->pw_expiration = kdb.pw_expiration;
696     if (mask & KADM5_MAX_LIFE)
697          entry->max_life = kdb.max_life;
698
699     /* this is a little non-sensical because the function returns two */
700     /* values that must be checked separately against the mask */
701     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
702         ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
703                                              &(entry->mod_date), 
704                                              &(entry->mod_name));
705         if (ret) {
706             goto done;
707         }
708         
709         if (! (mask & KADM5_MOD_TIME))
710             entry->mod_date = 0;
711         if (! (mask & KADM5_MOD_NAME)) {
712             krb5_free_principal(handle->context, entry->principal);
713             entry->principal = NULL;
714         }
715     }
716
717     if (mask & KADM5_ATTRIBUTES)
718          entry->attributes = kdb.attributes;
719
720     if (mask & KADM5_KVNO)
721          for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
722               if (kdb.key_data[i].key_data_kvno > entry->kvno)
723                    entry->kvno = kdb.key_data[i].key_data_kvno;
724     
725     if (handle->api_version == KADM5_API_VERSION_2)
726          entry->mkvno = 0;
727     else {
728          /* XXX I'll be damned if I know how to deal with this one --marc */
729          entry->mkvno = 1;
730     }
731
732     /*
733      * The new fields that only exist in version 2 start here
734      */
735     if (handle->api_version == KADM5_API_VERSION_2) {
736          if (mask & KADM5_MAX_RLIFE)
737               entry->max_renewable_life = kdb.max_renewable_life;
738          if (mask & KADM5_LAST_SUCCESS)
739               entry->last_success = kdb.last_success;
740          if (mask & KADM5_LAST_FAILED)
741               entry->last_failed = kdb.last_failed;
742          if (mask & KADM5_FAIL_AUTH_COUNT)
743               entry->fail_auth_count = kdb.fail_auth_count;
744          if (mask & KADM5_TL_DATA) {
745               krb5_tl_data *tl, *tl2;
746
747               entry->tl_data = NULL;
748               
749               tl = kdb.tl_data;
750               while (tl) {
751                    if (tl->tl_data_type > 255) {
752                         if ((tl2 = dup_tl_data(tl)) == NULL) {
753                              ret = ENOMEM;
754                              goto done;
755                         }
756                         tl2->tl_data_next = entry->tl_data;
757                         entry->tl_data = tl2;
758                         entry->n_tl_data++;
759                    }
760                         
761                    tl = tl->tl_data_next;
762               }
763          }
764          if (mask & KADM5_KEY_DATA) {
765               entry->n_key_data = kdb.n_key_data;
766               if(entry->n_key_data) {
767                       entry->key_data = (krb5_key_data *)
768                               malloc(entry->n_key_data*sizeof(krb5_key_data));
769                       if (entry->key_data == NULL) {
770                               ret = ENOMEM;
771                               goto done;
772                       }
773               } else 
774                       entry->key_data = NULL;
775
776               for (i = 0; i < entry->n_key_data; i++)
777                   ret = krb5_copy_key_data_contents(handle->context,
778                                                     &kdb.key_data[i],
779                                                     &entry->key_data[i]);
780                    if (ret)
781                         goto done;
782          }
783     }
784
785     /*
786      * If KADM5_API_VERSION_1, we return an allocated structure, and
787      * we need to convert the new structure back into the format the
788      * caller is expecting.
789      */
790     if (handle->api_version == KADM5_API_VERSION_1) {
791          kadm5_principal_ent_t_v1 newv1;
792
793          newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
794          if (newv1 == NULL) {
795               ret = ENOMEM;
796               goto done;
797          }
798          
799          newv1->principal = entry->principal;
800          newv1->princ_expire_time = entry->princ_expire_time;
801          newv1->last_pwd_change = entry->last_pwd_change;
802          newv1->pw_expiration = entry->pw_expiration;
803          newv1->max_life = entry->max_life;
804          newv1->mod_name = entry->mod_name;
805          newv1->mod_date = entry->mod_date;
806          newv1->attributes = entry->attributes;
807          newv1->kvno = entry->kvno;
808          newv1->mkvno = entry->mkvno;
809          newv1->policy = entry->policy;
810          newv1->aux_attributes = entry->aux_attributes;
811
812          *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
813     }
814
815     ret = KADM5_OK;
816
817 done:
818     if (ret && entry->principal)
819          krb5_free_principal(handle->context, entry->principal);
820     kdb_free_entry(handle, &kdb, &adb);
821
822     return ret;
823 }
824
825 /*
826  * Function: check_pw_reuse
827  *
828  * Purpose: Check if a key appears in a list of keys, in order to
829  * enforce password history.
830  *
831  * Arguments:
832  *
833  *      context                 (r) the krb5 context
834  *      hist_keyblock           (r) the key that hist_key_data is
835  *                              encrypted in
836  *      n_new_key_data          (r) length of new_key_data
837  *      new_key_data            (r) keys to check against
838  *                              pw_hist_data, encrypted in hist_keyblock
839  *      n_pw_hist_data          (r) length of pw_hist_data
840  *      pw_hist_data            (r) passwords to check new_key_data against
841  *
842  * Effects:
843  * For each new_key in new_key_data:
844  *      decrypt new_key with the master_keyblock
845  *      for each password in pw_hist_data:
846  *              for each hist_key in password:
847  *                      decrypt hist_key with hist_keyblock
848  *                      compare the new_key and hist_key
849  *
850  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
851  * new_key_data is the same as a key in pw_hist_data, or 0.
852  */
853 static kadm5_ret_t
854 check_pw_reuse(krb5_context context,
855                krb5_keyblock *hist_keyblock,
856                int n_new_key_data, krb5_key_data *new_key_data,
857                unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
858 {
859     int x, y, z;
860     krb5_keyblock newkey, histkey;
861     krb5_error_code ret;
862
863     for (x = 0; x < n_new_key_data; x++) {
864         ret = krb5_dbekd_decrypt_key_data(context,
865                                           &master_keyblock,
866                                           &(new_key_data[x]),
867                                           &newkey, NULL);
868         if (ret)
869             return(ret);
870         for (y = 0; y < n_pw_hist_data; y++) {
871              for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
872                  ret = krb5_dbekd_decrypt_key_data(context,
873                                                    hist_keyblock,
874                                                    &pw_hist_data[y].key_data[z],
875                                                    &histkey, NULL);
876                  if (ret)
877                      return(ret);               
878                  
879                  if ((newkey.length == histkey.length) &&
880                      (newkey.enctype == histkey.enctype) &&
881                      (memcmp(newkey.contents, histkey.contents,
882                              histkey.length) == 0)) {
883                      krb5_free_keyblock_contents(context, &histkey);
884                      krb5_free_keyblock_contents(context, &newkey);
885                      
886                      return(KADM5_PASS_REUSE);
887                  }
888                  krb5_free_keyblock_contents(context, &histkey);
889              }
890         }
891         krb5_free_keyblock_contents(context, &newkey);
892     }
893
894     return(0);
895 }
896
897 /*
898  * Function: create_history_entry
899  *
900  * Purpose: Creates a password history entry from an array of
901  * key_data.
902  *
903  * Arguments:
904  *
905  *      context         (r) krb5_context to use
906  *      n_key_data      (r) number of elements in key_data
907  *      key_data        (r) keys to add to the history entry
908  *      hist            (w) history entry to fill in
909  *
910  * Effects:
911  *
912  * hist->key_data is allocated to store n_key_data key_datas.  Each
913  * element of key_data is decrypted with master_keyblock, re-encrypted
914  * in hist_key, and added to hist->key_data.  hist->n_key_data is
915  * set to n_key_data.
916  */
917 static
918 int create_history_entry(krb5_context context, int n_key_data,
919                          krb5_key_data *key_data, osa_pw_hist_ent *hist)
920 {
921      int i, ret;
922      krb5_keyblock key;
923      krb5_keysalt salt;
924      
925      hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
926      if (hist->key_data == NULL)
927           return ENOMEM;
928      memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
929
930      for (i = 0; i < n_key_data; i++) {
931          ret = krb5_dbekd_decrypt_key_data(context,
932                                            &master_keyblock,
933                                            &key_data[i],
934                                            &key, &salt);
935          if (ret)
936              return ret;
937
938          ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
939                                            &key, &salt,
940                                            key_data[i].key_data_kvno,
941                                            &hist->key_data[i]);
942          if (ret)
943              return ret;
944          
945          krb5_free_keyblock_contents(context, &key);
946          /* krb5_free_keysalt(context, &salt); */
947      }
948
949      hist->n_key_data = n_key_data;
950      return 0;
951 }
952
953 static
954 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
955 {
956      int i;
957
958      for (i = 0; i < hist->n_key_data; i++)
959           krb5_free_key_data_contents(context, &hist->key_data[i]);
960      free(hist->key_data);
961 }
962
963 /*
964  * Function: add_to_history
965  *
966  * Purpose: Adds a password to a principal's password history.
967  *
968  * Arguments:
969  *
970  *      context         (r) krb5_context to use
971  *      adb             (r/w) admin principal entry to add keys to
972  *      pol             (r) adb's policy
973  *      pw              (r) keys for the password to add to adb's key history
974  *
975  * Effects:
976  *
977  * add_to_history adds a single password to adb's password history.
978  * pw contains n_key_data keys in its key_data, in storage should be
979  * allocated but not freed by the caller (XXX blech!).
980  *
981  * This function maintains adb->old_keys as a circular queue.  It
982  * starts empty, and grows each time this function is called until it
983  * is pol->pw_history_num items long.  adb->old_key_len holds the
984  * number of allocated entries in the array, and must therefore be [0,
985  * pol->pw_history_num).  adb->old_key_next is the index into the
986  * array where the next element should be written, and must be [0,
987  * adb->old_key_len).
988  */
989 #define KADM_MOD(x) (x + adb->old_key_next) % adb->old_key_len
990 static kadm5_ret_t add_to_history(krb5_context context,
991                                   osa_princ_ent_t adb,
992                                   kadm5_policy_ent_t pol,
993                                   osa_pw_hist_ent *pw)
994 {
995      osa_pw_hist_ent *histp;
996      int i;
997
998      /* A history of 1 means just check the current password */
999      if (pol->pw_history_num == 1)
1000           return 0;
1001
1002      /* resize the adb->old_keys array if necessary */
1003      if (adb->old_key_len < pol->pw_history_num-1) {
1004           if (adb->old_keys == NULL) {
1005                adb->old_keys = (osa_pw_hist_ent *)
1006                     malloc((adb->old_key_len + 1) * sizeof (osa_pw_hist_ent));
1007           } else {
1008                adb->old_keys = (osa_pw_hist_ent *)
1009                     realloc(adb->old_keys,
1010                             (adb->old_key_len + 1) * sizeof (osa_pw_hist_ent));
1011           }
1012           if (adb->old_keys == NULL)
1013                return(ENOMEM);
1014           
1015           memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent)); 
1016           adb->old_key_len++;
1017      } else if (adb->old_key_len > pol->pw_history_num-1) {
1018          /*
1019           * The policy must have changed!  Shrink the array.
1020           * Can't simply realloc() down, since it might be wrapped.
1021           * To understand the arithmetic below, note that we are
1022           * copying into new positions 0 .. N-1 from old positions
1023           * old_key_next-N .. old_key_next-1, modulo old_key_len,
1024           * where N = pw_history_num - 1 is the length of the
1025           * shortened list.        Matt Crawford, FNAL
1026           */
1027          int j;
1028          histp = (osa_pw_hist_ent *)
1029              malloc((pol->pw_history_num - 1) * sizeof (osa_pw_hist_ent));
1030          if (histp) {
1031              for (i = 0; i < pol->pw_history_num - 1; i++) {
1032                  /* We need the number we use the modulus operator on to be
1033                     positive, so after subtracting pol->pw_history_num-1, we
1034                     add back adb->old_key_len. */
1035                  j = KADM_MOD(i - (pol->pw_history_num - 1) + adb->old_key_len);
1036                  histp[i] = adb->old_keys[j];
1037              }
1038              /* Now free the ones we don't keep (the oldest ones) */
1039              for (i = 0; i < adb->old_key_len - (pol->pw_history_num - 1); i++)
1040                  for (j = 0; j < adb->old_keys[KADM_MOD(i)].n_key_data; j++)
1041                      krb5_free_key_data_contents(context,
1042                                 &adb->old_keys[KADM_MOD(i)].key_data[j]);
1043              free((void *)adb->old_keys);
1044              adb->old_keys = histp;
1045              adb->old_key_len = pol->pw_history_num - 1;
1046              adb->old_key_next = 0;
1047          } else {
1048              return(ENOMEM);
1049          }
1050      }
1051
1052      /* free the old pw history entry if it contains data */
1053      histp = &adb->old_keys[adb->old_key_next];
1054      for (i = 0; i < histp->n_key_data; i++)
1055           krb5_free_key_data_contents(context, &histp->key_data[i]);
1056      
1057      /* store the new entry */
1058      adb->old_keys[adb->old_key_next] = *pw;
1059
1060      /* update the next pointer */
1061      if (++adb->old_key_next == pol->pw_history_num-1)
1062                adb->old_key_next = 0;
1063
1064      return(0);
1065 }
1066 #undef KADM_MOD
1067
1068 kadm5_ret_t
1069 kadm5_chpass_principal(void *server_handle,
1070                             krb5_principal principal, char *password)
1071 {
1072     return
1073         kadm5_chpass_principal_3(server_handle, principal, FALSE,
1074                                  0, NULL, password);
1075 }
1076
1077 kadm5_ret_t
1078 kadm5_chpass_principal_3(void *server_handle,
1079                          krb5_principal principal, krb5_boolean keepold,
1080                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1081                          char *password)
1082 {
1083     krb5_int32                  now;
1084     kadm5_policy_ent_rec        pol;
1085     osa_princ_ent_rec           adb;
1086     krb5_db_entry               kdb, kdb_save;
1087     int                         ret, ret2, last_pwd, hist_added;
1088     int                         have_pol = 0;
1089     kadm5_server_handle_t       handle = server_handle;
1090     osa_pw_hist_ent             hist;
1091
1092     CHECK_HANDLE(server_handle);
1093
1094     hist_added = 0;
1095     memset(&hist, 0, sizeof(hist));
1096
1097     if (principal == NULL || password == NULL)
1098         return EINVAL;
1099     if ((krb5_principal_compare(handle->context,
1100                                 principal, hist_princ)) == TRUE)
1101         return KADM5_PROTECT_PRINCIPAL;
1102
1103     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1104        return(ret);
1105
1106     /* we are going to need the current keys after the new keys are set */
1107     if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1108          kdb_free_entry(handle, &kdb, &adb);
1109          return(ret);
1110     }
1111     
1112     if ((adb.aux_attributes & KADM5_POLICY)) {
1113         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1114              goto done;
1115         have_pol = 1;
1116     }
1117
1118     if ((ret = passwd_check(handle, password, adb.aux_attributes &
1119                             KADM5_POLICY, &pol, principal)))
1120          goto done;
1121
1122     ret = krb5_dbe_cpw(handle->context, &master_keyblock,
1123                        n_ks_tuple?ks_tuple:handle->params.keysalts,
1124                        n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1125                        password, 0 /* increment kvno */,
1126                        keepold, &kdb);
1127     if (ret)
1128         goto done;
1129
1130     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1131
1132     ret = krb5_timeofday(handle->context, &now);
1133     if (ret)
1134          goto done;
1135     
1136     if ((adb.aux_attributes & KADM5_POLICY)) {
1137        /* the policy was loaded before */
1138
1139         ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1140                                               &kdb, &last_pwd);
1141         if (ret)
1142             goto done;
1143
1144 #if 0
1145          /*
1146           * The spec says this check is overridden if the caller has
1147           * modify privilege.  The admin server therefore makes this
1148           * check itself (in chpass_principal_wrapper, misc.c). A
1149           * local caller implicitly has all authorization bits.
1150           */
1151         if ((now - last_pwd) < pol.pw_min_life &&
1152             !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1153              ret = KADM5_PASS_TOOSOON;
1154              goto done;
1155         }
1156 #endif
1157
1158         ret = create_history_entry(handle->context,
1159                                    kdb_save.n_key_data,
1160                                    kdb_save.key_data, &hist);
1161         if (ret)
1162             goto done;
1163
1164         ret = check_pw_reuse(handle->context, &hist_key,
1165                              kdb.n_key_data, kdb.key_data,
1166                              1, &hist);
1167         if (ret)
1168             goto done;
1169          
1170         if (pol.pw_history_num > 1) {
1171             if (adb.admin_history_kvno != hist_kvno) {
1172                 ret = KADM5_BAD_HIST_KEY;
1173                 goto done;
1174             }
1175
1176             ret = check_pw_reuse(handle->context, &hist_key,
1177                                  kdb.n_key_data, kdb.key_data,
1178                                  adb.old_key_len, adb.old_keys);
1179             if (ret)
1180                 goto done;
1181
1182             ret = add_to_history(handle->context, &adb, &pol, &hist);
1183             if (ret)
1184                 goto done;
1185             hist_added = 1;
1186        }
1187
1188         if (pol.pw_max_life)
1189            kdb.pw_expiration = now + pol.pw_max_life;
1190         else
1191            kdb.pw_expiration = 0;
1192     } else {
1193         kdb.pw_expiration = 0;
1194     }
1195
1196     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1197     if (ret)
1198         goto done;
1199
1200     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1201         goto done;
1202
1203     ret = KADM5_OK;
1204 done:
1205     if (!hist_added && hist.key_data)
1206          free_history_entry(handle->context, &hist);
1207     kdb_free_entry(handle, &kdb, &adb);
1208     kdb_free_entry(handle, &kdb_save, NULL);
1209     krb5_dbe_free_contents(handle->context, &kdb);
1210
1211     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1212         && !ret) 
1213          ret = ret2;
1214
1215     return ret;
1216 }
1217
1218 kadm5_ret_t
1219 kadm5_randkey_principal(void *server_handle,
1220                         krb5_principal principal,
1221                         krb5_keyblock **keyblocks,
1222                         int *n_keys)
1223 {
1224     return
1225         kadm5_randkey_principal_3(server_handle, principal,
1226                                   FALSE, 0, NULL,
1227                                   keyblocks, n_keys);
1228 }
1229 kadm5_ret_t
1230 kadm5_randkey_principal_3(void *server_handle,
1231                         krb5_principal principal,
1232                         krb5_boolean keepold,
1233                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1234                         krb5_keyblock **keyblocks,
1235                         int *n_keys)
1236 {
1237     krb5_db_entry               kdb;
1238     osa_princ_ent_rec           adb;
1239     krb5_int32                  now;
1240     kadm5_policy_ent_rec        pol;
1241     krb5_key_data               *key_data;
1242     int                         ret, last_pwd, have_pol = 0;
1243     kadm5_server_handle_t       handle = server_handle;
1244
1245     if (keyblocks)
1246          *keyblocks = NULL;
1247
1248     CHECK_HANDLE(server_handle);
1249
1250     if (principal == NULL)
1251         return EINVAL;
1252     if (hist_princ && /* this will be NULL when initializing the databse */
1253         ((krb5_principal_compare(handle->context,
1254                                  principal, hist_princ)) == TRUE))
1255         return KADM5_PROTECT_PRINCIPAL;
1256
1257     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1258        return(ret);
1259
1260     ret = krb5_dbe_crk(handle->context, &master_keyblock,
1261                        n_ks_tuple?ks_tuple:handle->params.keysalts,
1262                        n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1263                        keepold,
1264                        &kdb);
1265     if (ret)
1266         goto done;
1267
1268     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1269
1270     ret = krb5_timeofday(handle->context, &now);
1271     if (ret)
1272         goto done;
1273
1274     if ((adb.aux_attributes & KADM5_POLICY)) {
1275         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1276                                     &pol)) != KADM5_OK) 
1277            goto done;
1278         have_pol = 1;
1279
1280         ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1281                                               &kdb, &last_pwd);
1282         if (ret)
1283              goto done;
1284
1285 #if 0
1286          /*
1287           * The spec says this check is overridden if the caller has
1288           * modify privilege.  The admin server therefore makes this
1289           * check itself (in chpass_principal_wrapper, misc.c).  A
1290           * local caller implicitly has all authorization bits.
1291           */
1292         if((now - last_pwd) < pol.pw_min_life &&
1293            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1294              ret = KADM5_PASS_TOOSOON;
1295              goto done;
1296         }
1297 #endif
1298
1299         if(pol.pw_history_num > 1) {
1300             if(adb.admin_history_kvno != hist_kvno) {
1301                 ret = KADM5_BAD_HIST_KEY;
1302                 goto done;
1303             }
1304
1305             ret = check_pw_reuse(handle->context, &hist_key,
1306                                  kdb.n_key_data, kdb.key_data,
1307                                  adb.old_key_len, adb.old_keys);
1308             if (ret)
1309                 goto done;
1310         }
1311         if (pol.pw_max_life)
1312            kdb.pw_expiration = now + pol.pw_max_life;
1313         else
1314            kdb.pw_expiration = 0;
1315     } else {
1316         kdb.pw_expiration = 0;
1317     }
1318
1319     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1320     if (ret)
1321          goto done;
1322
1323     if (keyblocks) {
1324          if (handle->api_version == KADM5_API_VERSION_1) {
1325               /* Version 1 clients will expect to see a DES_CRC enctype. */
1326              ret = krb5_dbe_find_enctype(handle->context, &kdb,
1327                                          ENCTYPE_DES_CBC_CRC,
1328                                          -1, -1, &key_data);
1329              if (ret)
1330                  goto done;
1331              
1332              ret = decrypt_key_data(handle->context, 1, key_data,
1333                                      keyblocks, NULL);
1334              if (ret)
1335                  goto done;
1336          } else {
1337              ret = decrypt_key_data(handle->context,
1338                                      kdb.n_key_data, kdb.key_data,
1339                                      keyblocks, n_keys);
1340              if (ret)
1341                  goto done;
1342          }
1343     }    
1344     
1345     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1346         goto done;
1347
1348     ret = KADM5_OK;
1349 done:
1350     kdb_free_entry(handle, &kdb, &adb);
1351     if (have_pol)
1352          kadm5_free_policy_ent(handle->lhandle, &pol);
1353
1354     return ret;
1355 }
1356
1357 /*
1358  * kadm5_setv4key_principal:
1359  *
1360  * Set only ONE key of the principal, removing all others.  This key
1361  * must have the DES_CBC_CRC enctype and is entered as having the
1362  * krb4 salttype.  This is to enable things like kadmind4 to work.
1363  */
1364 kadm5_ret_t
1365 kadm5_setv4key_principal(void *server_handle,
1366                        krb5_principal principal,
1367                        krb5_keyblock *keyblock)
1368 {
1369     krb5_db_entry               kdb;
1370     osa_princ_ent_rec           adb;
1371     krb5_int32                  now;
1372     kadm5_policy_ent_rec        pol;
1373     krb5_keysalt                keysalt;
1374     int                         i, kvno, ret, have_pol = 0;
1375 #if 0
1376     int                         last_pwd;
1377 #endif
1378     kadm5_server_handle_t       handle = server_handle;
1379
1380     CHECK_HANDLE(server_handle);
1381
1382     if (principal == NULL || keyblock == NULL)
1383         return EINVAL;
1384     if (hist_princ && /* this will be NULL when initializing the databse */
1385         ((krb5_principal_compare(handle->context,
1386                                  principal, hist_princ)) == TRUE))
1387         return KADM5_PROTECT_PRINCIPAL;
1388
1389     if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1390         return KADM5_SETV4KEY_INVAL_ENCTYPE;
1391     
1392     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1393        return(ret);
1394
1395     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1396          if (kdb.key_data[i].key_data_kvno > kvno)
1397               kvno = kdb.key_data[i].key_data_kvno;
1398
1399     if (kdb.key_data != NULL)
1400          cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1401     
1402     kdb.key_data = (krb5_key_data*)malloc(sizeof(krb5_key_data));
1403     if (kdb.key_data == NULL)
1404          return ENOMEM;
1405     memset(kdb.key_data, 0, sizeof(krb5_key_data));
1406     kdb.n_key_data = 1;
1407     keysalt.type = KRB5_KDB_SALTTYPE_V4;
1408     /* XXX data.magic? */
1409     keysalt.data.length = 0;
1410     keysalt.data.data = NULL;
1411
1412     ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock,
1413                                       keyblock, &keysalt, kvno + 1,
1414                                       kdb.key_data);
1415     if (ret) {
1416         goto done;
1417     }
1418
1419     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1420
1421     ret = krb5_timeofday(handle->context, &now);
1422     if (ret)
1423         goto done;
1424
1425     if ((adb.aux_attributes & KADM5_POLICY)) {
1426         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1427                                     &pol)) != KADM5_OK) 
1428            goto done;
1429         have_pol = 1;
1430
1431 #if 0
1432         /*
1433           * The spec says this check is overridden if the caller has
1434           * modify privilege.  The admin server therefore makes this
1435           * check itself (in chpass_principal_wrapper, misc.c).  A
1436           * local caller implicitly has all authorization bits.
1437           */
1438         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1439                                                   &kdb, &last_pwd))
1440              goto done;
1441         if((now - last_pwd) < pol.pw_min_life &&
1442            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1443              ret = KADM5_PASS_TOOSOON;
1444              goto done;
1445         }
1446 #endif
1447 #if 0
1448         /*
1449          * Should we be checking/updating pw history here?
1450          */
1451         if(pol.pw_history_num > 1) {
1452             if(adb.admin_history_kvno != hist_kvno) {
1453                 ret = KADM5_BAD_HIST_KEY;
1454                 goto done;
1455             }
1456
1457             if (ret = check_pw_reuse(handle->context,
1458                                      &hist_key,
1459                                      kdb.n_key_data, kdb.key_data,
1460                                      adb.old_key_len, adb.old_keys))
1461                 goto done;
1462         }
1463 #endif
1464         
1465         if (pol.pw_max_life)
1466            kdb.pw_expiration = now + pol.pw_max_life;
1467         else
1468            kdb.pw_expiration = 0;
1469     } else {
1470         kdb.pw_expiration = 0;
1471     }
1472
1473     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1474     if (ret)
1475          goto done;
1476
1477     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1478         goto done;
1479
1480     ret = KADM5_OK;
1481 done:
1482     kdb_free_entry(handle, &kdb, &adb);
1483     if (have_pol)
1484          kadm5_free_policy_ent(handle->lhandle, &pol);
1485
1486     return ret;
1487 }
1488
1489 kadm5_ret_t
1490 kadm5_setkey_principal(void *server_handle,
1491                        krb5_principal principal,
1492                        krb5_keyblock *keyblocks,
1493                        int n_keys)
1494 {
1495     return
1496         kadm5_setkey_principal_3(server_handle, principal,
1497                                  FALSE, 0, NULL,
1498                                  keyblocks, n_keys);
1499 }
1500
1501 kadm5_ret_t
1502 kadm5_setkey_principal_3(void *server_handle,
1503                          krb5_principal principal,
1504                          krb5_boolean keepold,
1505                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1506                          krb5_keyblock *keyblocks,
1507                          int n_keys)
1508 {
1509     krb5_db_entry               kdb;
1510     osa_princ_ent_rec           adb;
1511     krb5_int32                  now;
1512     kadm5_policy_ent_rec        pol;
1513     krb5_key_data               *old_key_data;
1514     int                         n_old_keys;
1515     int                         i, j, kvno, ret, have_pol = 0;
1516 #if 0
1517     int                         last_pwd;
1518 #endif
1519     kadm5_server_handle_t       handle = server_handle;
1520     krb5_boolean                similar;
1521     krb5_keysalt                keysalt;
1522
1523     CHECK_HANDLE(server_handle);
1524
1525     if (principal == NULL || keyblocks == NULL)
1526         return EINVAL;
1527     if (hist_princ && /* this will be NULL when initializing the databse */
1528         ((krb5_principal_compare(handle->context,
1529                                  principal, hist_princ)) == TRUE))
1530         return KADM5_PROTECT_PRINCIPAL;
1531
1532     for (i = 0; i < n_keys; i++) {
1533         for (j = i+1; j < n_keys; j++) {
1534             if ((ret = krb5_c_enctype_compare(handle->context,
1535                                               keyblocks[i].enctype,
1536                                               keyblocks[j].enctype,
1537                                               &similar)))
1538                 return(ret);
1539             if (similar) {
1540                 if (n_ks_tuple) {
1541                     if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1542                         return KADM5_SETKEY_DUP_ENCTYPES;
1543                 } else
1544                     return KADM5_SETKEY_DUP_ENCTYPES;
1545             }
1546         }
1547     }
1548
1549     if (n_ks_tuple && n_ks_tuple != n_keys)
1550         return KADM5_SETKEY3_ETYPE_MISMATCH;
1551
1552     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1553        return(ret);
1554     
1555     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1556          if (kdb.key_data[i].key_data_kvno > kvno)
1557               kvno = kdb.key_data[i].key_data_kvno;
1558
1559     if (keepold) {
1560         old_key_data = kdb.key_data;
1561         n_old_keys = kdb.n_key_data;
1562     } else {
1563         if (kdb.key_data != NULL)
1564             cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1565         n_old_keys = 0;
1566         old_key_data = NULL;
1567     }
1568     
1569     kdb.key_data = (krb5_key_data*)malloc((n_keys+n_old_keys)
1570                                           *sizeof(krb5_key_data));
1571     if (kdb.key_data == NULL)
1572          return ENOMEM;
1573     memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1574     kdb.n_key_data = 0;
1575
1576     for (i = 0; i < n_keys; i++) {
1577         if (n_ks_tuple) {
1578             keysalt.type = ks_tuple[i].ks_salttype;
1579             keysalt.data.length = 0;
1580             keysalt.data.data = NULL;
1581             if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1582                 cleanup_key_data(handle->context, kdb.n_key_data,
1583                                  kdb.key_data);
1584                 return KADM5_SETKEY3_ETYPE_MISMATCH;
1585             }
1586         }
1587         ret = krb5_dbekd_encrypt_key_data(handle->context,
1588                                           &master_keyblock,
1589                                           &keyblocks[i],
1590                                           n_ks_tuple ? &keysalt : NULL,
1591                                           kvno + 1,
1592                                           &kdb.key_data[i]);
1593         if (ret) {
1594             cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1595             return ret;
1596         }
1597         kdb.n_key_data++;
1598     }
1599
1600     /* copy old key data if necessary */
1601     for (i = 0; i < n_old_keys; i++) {
1602         kdb.key_data[i+n_keys] = old_key_data[i];
1603         memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1604         kdb.n_key_data++;
1605     }
1606     /* assert(kdb.n_key_data == n_keys + n_old_keys) */
1607     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1608
1609     if ((ret = krb5_timeofday(handle->context, &now)))
1610         goto done;
1611
1612     if ((adb.aux_attributes & KADM5_POLICY)) {
1613         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1614                                     &pol)) != KADM5_OK) 
1615            goto done;
1616         have_pol = 1;
1617
1618 #if 0
1619         /*
1620           * The spec says this check is overridden if the caller has
1621           * modify privilege.  The admin server therefore makes this
1622           * check itself (in chpass_principal_wrapper, misc.c).  A
1623           * local caller implicitly has all authorization bits.
1624           */
1625         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1626                                                   &kdb, &last_pwd))
1627              goto done;
1628         if((now - last_pwd) < pol.pw_min_life &&
1629            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1630              ret = KADM5_PASS_TOOSOON;
1631              goto done;
1632         }
1633 #endif
1634 #if 0
1635         /*
1636          * Should we be checking/updating pw history here?
1637          */
1638         if(pol.pw_history_num > 1) {
1639             if(adb.admin_history_kvno != hist_kvno) {
1640                 ret = KADM5_BAD_HIST_KEY;
1641                 goto done;
1642             }
1643
1644             if (ret = check_pw_reuse(handle->context,
1645                                      &hist_key,
1646                                      kdb.n_key_data, kdb.key_data,
1647                                      adb.old_key_len, adb.old_keys))
1648                 goto done;
1649         }
1650 #endif
1651         
1652         if (pol.pw_max_life)
1653            kdb.pw_expiration = now + pol.pw_max_life;
1654         else
1655            kdb.pw_expiration = 0;
1656     } else {
1657         kdb.pw_expiration = 0;
1658     }
1659
1660     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
1661         goto done;
1662
1663     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1664         goto done;
1665
1666     ret = KADM5_OK;
1667 done:
1668     kdb_free_entry(handle, &kdb, &adb);
1669     if (have_pol)
1670          kadm5_free_policy_ent(handle->lhandle, &pol);
1671
1672     return ret;
1673 }
1674
1675 /*
1676  * Allocate an array of n_key_data krb5_keyblocks, fill in each
1677  * element with the results of decrypting the nth key in key_data with
1678  * master_keyblock, and if n_keys is not NULL fill it in with the
1679  * number of keys decrypted.
1680  */
1681 static int decrypt_key_data(krb5_context context,
1682                             int n_key_data, krb5_key_data *key_data,
1683                             krb5_keyblock **keyblocks, int *n_keys)
1684 {
1685      krb5_keyblock *keys;
1686      int ret, i;
1687
1688      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1689      if (keys == NULL)
1690           return ENOMEM;
1691      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1692
1693      for (i = 0; i < n_key_data; i++) {
1694           ret = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
1695                                             &key_data[i], 
1696                                             &keys[i], NULL);
1697           if (ret) {
1698
1699                memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1700                free(keys);
1701                return ret;
1702           }
1703      }
1704
1705      *keyblocks = keys;
1706      if (n_keys)
1707           *n_keys = n_key_data;
1708
1709      return 0;
1710 }
1711
1712 /*
1713  * Function: kadm5_decrypt_key
1714  *
1715  * Purpose: Retrieves and decrypts a principal key.
1716  *
1717  * Arguments:
1718  *
1719  *      server_handle   (r) kadm5 handle
1720  *      entry           (r) principal retrieved with kadm5_get_principal
1721  *      ktype           (r) enctype to search for, or -1 to ignore
1722  *      stype           (r) salt type to search for, or -1 to ignore
1723  *      kvno            (r) kvno to search for, -1 for max, 0 for max
1724  *                      only if it also matches ktype and stype
1725  *      keyblock        (w) keyblock to fill in
1726  *      keysalt         (w) keysalt to fill in, or NULL
1727  *      kvnop           (w) kvno to fill in, or NULL
1728  *
1729  * Effects: Searches the key_data array of entry, which must have been
1730  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1731  * find a key with a specified enctype, salt type, and kvno in a
1732  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
1733  * it with the master key, and return the key in keyblock, the salt
1734  * in salttype, and the key version number in kvno.
1735  *
1736  * If ktype or stype is -1, it is ignored for the search.  If kvno is
1737  * -1, ktype and stype are ignored and the key with the max kvno is
1738  * returned.  If kvno is 0, only the key with the max kvno is returned
1739  * and only if it matches the ktype and stype; otherwise, ENOENT is
1740  * returned.
1741  */
1742 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1743                               kadm5_principal_ent_t entry, krb5_int32
1744                               ktype, krb5_int32 stype, krb5_int32
1745                               kvno, krb5_keyblock *keyblock,
1746                               krb5_keysalt *keysalt, int *kvnop)
1747 {
1748     kadm5_server_handle_t handle = server_handle;
1749     krb5_db_entry dbent;
1750     krb5_key_data *key_data;
1751     int ret;
1752
1753     CHECK_HANDLE(server_handle);
1754
1755     if (entry->n_key_data == 0 || entry->key_data == NULL)
1756          return EINVAL;
1757
1758     /* find_enctype only uses these two fields */
1759     dbent.n_key_data = entry->n_key_data;
1760     dbent.key_data = entry->key_data;
1761     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1762                                     stype, kvno, &key_data)))
1763          return ret;
1764
1765     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
1766                                            &master_keyblock, key_data,
1767                                            keyblock, keysalt)))
1768          return ret;
1769
1770     /*
1771      * Coerce the enctype of the output keyblock in case we got an
1772      * inexact match on the enctype; this behavior will go away when
1773      * the key storage architecture gets redesigned for 1.3.
1774      */
1775     keyblock->enctype = ktype;
1776
1777     if (kvnop)
1778          *kvnop = key_data->key_data_kvno;
1779
1780     return KADM5_OK;
1781 }