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