Fixed comment (part of adding 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).
1093  *
1094  * Because a failure to communicate with the tool results in the
1095  * password server falling out of sync with the database,
1096  * kadm5_launch_task() always fails if it can't talk to the tool.
1097  */
1098
1099 static kadm5_ret_t
1100 kadm5_launch_task (krb5_context context,
1101                    const char *task_path, char * const task_argv[],
1102                    const char *data_buffer) 
1103 {
1104     kadm5_ret_t ret = 0;
1105     int data_pipe[2];
1106     
1107     if (data_buffer != NULL) {
1108         ret = pipe (data_pipe);
1109         if (ret) { ret = errno; }
1110     }
1111
1112     if (!ret) {
1113         pid_t pid = fork ();
1114         if (pid == -1) {
1115             ret = errno;
1116         } else if (pid == 0) {
1117             /* The child: */
1118             
1119             if (data_buffer != NULL) {
1120                 if (dup2 (data_pipe[0], STDIN_FILENO) == -1) {
1121                     _exit (1);
1122                 }
1123             } else {
1124                 close (data_pipe[0]);
1125             }
1126
1127             close (data_pipe[1]);
1128             
1129             execv (task_path, task_argv);
1130             
1131             _exit (1); /* Fail if execv fails */
1132         } else {
1133             /* The parent: */
1134             
1135             if (data_buffer != NULL) {
1136                 /* Write out the buffer to the child */
1137                 if (krb5_net_write (context, data_pipe[1],
1138                                     data_buffer, strlen (data_buffer)) < 0) {
1139                     ret = errno;
1140                 }
1141             }
1142
1143             close (data_buffer[0]);
1144             close (data_buffer[1]);
1145
1146             if (!ret) {
1147                 int status = 0;
1148
1149                 waitpid (pid, &status, 0);
1150
1151                 /* fprintf (stderr, "Call \"%s\" returned status %d\n", argv[2],
1152                     WEXITSTATUS(status)); */
1153
1154                 if (WIFEXITED (status)) {
1155                     /* task finished.  Check return value */
1156                     if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
1157                        ret = KRB5KDC_ERR_POLICY; /* password change rejected */
1158                     }
1159                 } else {
1160                     /* task crashed or was killed */
1161                     ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1162                 }
1163             } else {
1164                 /* since the password write failed, just try and kill the child
1165                  * process in order to clean up (it may already be gone). */
1166                 kill (pid, SIGKILL);
1167             }
1168         }
1169     }
1170
1171     return ret;
1172 }
1173
1174 #endif
1175
1176 kadm5_ret_t
1177 kadm5_chpass_principal(void *server_handle,
1178                             krb5_principal principal, char *password)
1179 {
1180     return
1181         kadm5_chpass_principal_3(server_handle, principal, FALSE,
1182                                  0, NULL, password);
1183 }
1184
1185 kadm5_ret_t
1186 kadm5_chpass_principal_3(void *server_handle,
1187                          krb5_principal principal, krb5_boolean keepold,
1188                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1189                          char *password)
1190 {
1191     krb5_int32                  now;
1192     kadm5_policy_ent_rec        pol;
1193     osa_princ_ent_rec           adb;
1194     krb5_db_entry               kdb, kdb_save;
1195     int                         ret, ret2, last_pwd, hist_added;
1196     int                         have_pol = 0;
1197     kadm5_server_handle_t       handle = server_handle;
1198     osa_pw_hist_ent             hist;
1199
1200     CHECK_HANDLE(server_handle);
1201
1202     hist_added = 0;
1203     memset(&hist, 0, sizeof(hist));
1204
1205     if (principal == NULL || password == NULL)
1206         return EINVAL;
1207     if ((krb5_principal_compare(handle->context,
1208                                 principal, hist_princ)) == TRUE)
1209         return KADM5_PROTECT_PRINCIPAL;
1210
1211     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1212        return(ret);
1213
1214     /* we are going to need the current keys after the new keys are set */
1215     if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1216          kdb_free_entry(handle, &kdb, &adb);
1217          return(ret);
1218     }
1219     
1220     if ((adb.aux_attributes & KADM5_POLICY)) {
1221         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1222              goto done;
1223         have_pol = 1;
1224     }
1225
1226     if ((ret = passwd_check(handle, password, adb.aux_attributes &
1227                             KADM5_POLICY, &pol, principal)))
1228          goto done;
1229
1230     ret = krb5_dbe_cpw(handle->context, &master_keyblock,
1231                        n_ks_tuple?ks_tuple:handle->params.keysalts,
1232                        n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1233                        password, 0 /* increment kvno */,
1234                        keepold, &kdb);
1235     if (ret)
1236         goto done;
1237
1238     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1239
1240     ret = krb5_timeofday(handle->context, &now);
1241     if (ret)
1242          goto done;
1243     
1244     if ((adb.aux_attributes & KADM5_POLICY)) {
1245        /* the policy was loaded before */
1246
1247         ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1248                                               &kdb, &last_pwd);
1249         if (ret)
1250             goto done;
1251
1252 #if 0
1253          /*
1254           * The spec says this check is overridden if the caller has
1255           * modify privilege.  The admin server therefore makes this
1256           * check itself (in chpass_principal_wrapper, misc.c). A
1257           * local caller implicitly has all authorization bits.
1258           */
1259         if ((now - last_pwd) < pol.pw_min_life &&
1260             !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1261              ret = KADM5_PASS_TOOSOON;
1262              goto done;
1263         }
1264 #endif
1265
1266         ret = create_history_entry(handle->context,
1267                                    kdb_save.n_key_data,
1268                                    kdb_save.key_data, &hist);
1269         if (ret)
1270             goto done;
1271
1272         ret = check_pw_reuse(handle->context, &hist_key,
1273                              kdb.n_key_data, kdb.key_data,
1274                              1, &hist);
1275         if (ret)
1276             goto done;
1277          
1278         if (pol.pw_history_num > 1) {
1279             if (adb.admin_history_kvno != hist_kvno) {
1280                 ret = KADM5_BAD_HIST_KEY;
1281                 goto done;
1282             }
1283
1284             ret = check_pw_reuse(handle->context, &hist_key,
1285                                  kdb.n_key_data, kdb.key_data,
1286                                  adb.old_key_len, adb.old_keys);
1287             if (ret)
1288                 goto done;
1289
1290             ret = add_to_history(handle->context, &adb, &pol, &hist);
1291             if (ret)
1292                 goto done;
1293             hist_added = 1;
1294        }
1295
1296         if (pol.pw_max_life)
1297            kdb.pw_expiration = now + pol.pw_max_life;
1298         else
1299            kdb.pw_expiration = 0;
1300     } else {
1301         kdb.pw_expiration = 0;
1302     }
1303
1304 #ifdef USE_PASSWORD_SERVER
1305     if (kadm5_use_password_server () &&
1306         (krb5_princ_size (handle->context, principal) == 1)) {
1307         krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
1308         const char *path = "/usr/sbin/mkpassdb";
1309         char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
1310         char *pstring = NULL;
1311         char pwbuf[256];
1312         int pwlen = strlen (password);
1313
1314         if (pwlen > 254) pwlen = 254;
1315         strncpy (pwbuf, password, pwlen);
1316         pwbuf[pwlen] = '\n';
1317         pwbuf[pwlen + 1] = '\0';
1318
1319         if (!ret) {
1320             pstring = malloc ((princ->length + 1) * sizeof (char));
1321             if (pstring == NULL) { ret = errno; }
1322         }
1323
1324         if (!ret) {
1325             memcpy (pstring, princ->data, princ->length);
1326             pstring [princ->length] = '\0';
1327             argv[2] = pstring;
1328
1329             ret = kadm5_launch_task (handle->context, path, argv, pwbuf);
1330         }
1331         
1332         if (pstring != NULL)
1333             free (pstring);
1334         
1335         if (ret)
1336             goto done;
1337     }
1338 #endif
1339
1340     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1341     if (ret)
1342         goto done;
1343
1344     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1345         goto done;
1346
1347     ret = KADM5_OK;
1348 done:
1349     if (!hist_added && hist.key_data)
1350          free_history_entry(handle->context, &hist);
1351     kdb_free_entry(handle, &kdb, &adb);
1352     kdb_free_entry(handle, &kdb_save, NULL);
1353     krb5_dbe_free_contents(handle->context, &kdb);
1354
1355     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1356         && !ret) 
1357          ret = ret2;
1358
1359     return ret;
1360 }
1361
1362 kadm5_ret_t
1363 kadm5_randkey_principal(void *server_handle,
1364                         krb5_principal principal,
1365                         krb5_keyblock **keyblocks,
1366                         int *n_keys)
1367 {
1368     return
1369         kadm5_randkey_principal_3(server_handle, principal,
1370                                   FALSE, 0, NULL,
1371                                   keyblocks, n_keys);
1372 }
1373 kadm5_ret_t
1374 kadm5_randkey_principal_3(void *server_handle,
1375                         krb5_principal principal,
1376                         krb5_boolean keepold,
1377                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1378                         krb5_keyblock **keyblocks,
1379                         int *n_keys)
1380 {
1381     krb5_db_entry               kdb;
1382     osa_princ_ent_rec           adb;
1383     krb5_int32                  now;
1384     kadm5_policy_ent_rec        pol;
1385     krb5_key_data               *key_data;
1386     int                         ret, last_pwd, have_pol = 0;
1387     kadm5_server_handle_t       handle = server_handle;
1388
1389     if (keyblocks)
1390          *keyblocks = NULL;
1391
1392     CHECK_HANDLE(server_handle);
1393
1394     if (principal == NULL)
1395         return EINVAL;
1396     if (hist_princ && /* this will be NULL when initializing the databse */
1397         ((krb5_principal_compare(handle->context,
1398                                  principal, hist_princ)) == TRUE))
1399         return KADM5_PROTECT_PRINCIPAL;
1400
1401     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1402        return(ret);
1403
1404     ret = krb5_dbe_crk(handle->context, &master_keyblock,
1405                        n_ks_tuple?ks_tuple:handle->params.keysalts,
1406                        n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1407                        keepold,
1408                        &kdb);
1409     if (ret)
1410         goto done;
1411
1412     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1413
1414     ret = krb5_timeofday(handle->context, &now);
1415     if (ret)
1416         goto done;
1417
1418     if ((adb.aux_attributes & KADM5_POLICY)) {
1419         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1420                                     &pol)) != KADM5_OK) 
1421            goto done;
1422         have_pol = 1;
1423
1424         ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1425                                               &kdb, &last_pwd);
1426         if (ret)
1427              goto done;
1428
1429 #if 0
1430          /*
1431           * The spec says this check is overridden if the caller has
1432           * modify privilege.  The admin server therefore makes this
1433           * check itself (in chpass_principal_wrapper, misc.c).  A
1434           * local caller implicitly has all authorization bits.
1435           */
1436         if((now - last_pwd) < pol.pw_min_life &&
1437            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1438              ret = KADM5_PASS_TOOSOON;
1439              goto done;
1440         }
1441 #endif
1442
1443         if(pol.pw_history_num > 1) {
1444             if(adb.admin_history_kvno != hist_kvno) {
1445                 ret = KADM5_BAD_HIST_KEY;
1446                 goto done;
1447             }
1448
1449             ret = check_pw_reuse(handle->context, &hist_key,
1450                                  kdb.n_key_data, kdb.key_data,
1451                                  adb.old_key_len, adb.old_keys);
1452             if (ret)
1453                 goto done;
1454         }
1455         if (pol.pw_max_life)
1456            kdb.pw_expiration = now + pol.pw_max_life;
1457         else
1458            kdb.pw_expiration = 0;
1459     } else {
1460         kdb.pw_expiration = 0;
1461     }
1462
1463     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1464     if (ret)
1465          goto done;
1466
1467     if (keyblocks) {
1468          if (handle->api_version == KADM5_API_VERSION_1) {
1469               /* Version 1 clients will expect to see a DES_CRC enctype. */
1470              ret = krb5_dbe_find_enctype(handle->context, &kdb,
1471                                          ENCTYPE_DES_CBC_CRC,
1472                                          -1, -1, &key_data);
1473              if (ret)
1474                  goto done;
1475              
1476              ret = decrypt_key_data(handle->context, 1, key_data,
1477                                      keyblocks, NULL);
1478              if (ret)
1479                  goto done;
1480          } else {
1481              ret = decrypt_key_data(handle->context,
1482                                      kdb.n_key_data, kdb.key_data,
1483                                      keyblocks, n_keys);
1484              if (ret)
1485                  goto done;
1486          }
1487     }    
1488     
1489     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1490         goto done;
1491
1492     ret = KADM5_OK;
1493 done:
1494     kdb_free_entry(handle, &kdb, &adb);
1495     if (have_pol)
1496          kadm5_free_policy_ent(handle->lhandle, &pol);
1497
1498     return ret;
1499 }
1500
1501 /*
1502  * kadm5_setv4key_principal:
1503  *
1504  * Set only ONE key of the principal, removing all others.  This key
1505  * must have the DES_CBC_CRC enctype and is entered as having the
1506  * krb4 salttype.  This is to enable things like kadmind4 to work.
1507  */
1508 kadm5_ret_t
1509 kadm5_setv4key_principal(void *server_handle,
1510                        krb5_principal principal,
1511                        krb5_keyblock *keyblock)
1512 {
1513     krb5_db_entry               kdb;
1514     osa_princ_ent_rec           adb;
1515     krb5_int32                  now;
1516     kadm5_policy_ent_rec        pol;
1517     krb5_keysalt                keysalt;
1518     int                         i, kvno, ret, have_pol = 0;
1519 #if 0
1520     int                         last_pwd;
1521 #endif
1522     kadm5_server_handle_t       handle = server_handle;
1523
1524     CHECK_HANDLE(server_handle);
1525
1526     if (principal == NULL || keyblock == NULL)
1527         return EINVAL;
1528     if (hist_princ && /* this will be NULL when initializing the databse */
1529         ((krb5_principal_compare(handle->context,
1530                                  principal, hist_princ)) == TRUE))
1531         return KADM5_PROTECT_PRINCIPAL;
1532
1533     if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1534         return KADM5_SETV4KEY_INVAL_ENCTYPE;
1535     
1536     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1537        return(ret);
1538
1539     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1540          if (kdb.key_data[i].key_data_kvno > kvno)
1541               kvno = kdb.key_data[i].key_data_kvno;
1542
1543     if (kdb.key_data != NULL)
1544          cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1545     
1546     kdb.key_data = (krb5_key_data*)malloc(sizeof(krb5_key_data));
1547     if (kdb.key_data == NULL)
1548          return ENOMEM;
1549     memset(kdb.key_data, 0, sizeof(krb5_key_data));
1550     kdb.n_key_data = 1;
1551     keysalt.type = KRB5_KDB_SALTTYPE_V4;
1552     /* XXX data.magic? */
1553     keysalt.data.length = 0;
1554     keysalt.data.data = NULL;
1555
1556     ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock,
1557                                       keyblock, &keysalt, kvno + 1,
1558                                       kdb.key_data);
1559     if (ret) {
1560         goto done;
1561     }
1562
1563     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1564
1565     ret = krb5_timeofday(handle->context, &now);
1566     if (ret)
1567         goto done;
1568
1569     if ((adb.aux_attributes & KADM5_POLICY)) {
1570         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1571                                     &pol)) != KADM5_OK) 
1572            goto done;
1573         have_pol = 1;
1574
1575 #if 0
1576         /*
1577           * The spec says this check is overridden if the caller has
1578           * modify privilege.  The admin server therefore makes this
1579           * check itself (in chpass_principal_wrapper, misc.c).  A
1580           * local caller implicitly has all authorization bits.
1581           */
1582         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1583                                                   &kdb, &last_pwd))
1584              goto done;
1585         if((now - last_pwd) < pol.pw_min_life &&
1586            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1587              ret = KADM5_PASS_TOOSOON;
1588              goto done;
1589         }
1590 #endif
1591 #if 0
1592         /*
1593          * Should we be checking/updating pw history here?
1594          */
1595         if(pol.pw_history_num > 1) {
1596             if(adb.admin_history_kvno != hist_kvno) {
1597                 ret = KADM5_BAD_HIST_KEY;
1598                 goto done;
1599             }
1600
1601             if (ret = check_pw_reuse(handle->context,
1602                                      &hist_key,
1603                                      kdb.n_key_data, kdb.key_data,
1604                                      adb.old_key_len, adb.old_keys))
1605                 goto done;
1606         }
1607 #endif
1608         
1609         if (pol.pw_max_life)
1610            kdb.pw_expiration = now + pol.pw_max_life;
1611         else
1612            kdb.pw_expiration = 0;
1613     } else {
1614         kdb.pw_expiration = 0;
1615     }
1616
1617     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1618     if (ret)
1619          goto done;
1620
1621     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1622         goto done;
1623
1624     ret = KADM5_OK;
1625 done:
1626     kdb_free_entry(handle, &kdb, &adb);
1627     if (have_pol)
1628          kadm5_free_policy_ent(handle->lhandle, &pol);
1629
1630     return ret;
1631 }
1632
1633 kadm5_ret_t
1634 kadm5_setkey_principal(void *server_handle,
1635                        krb5_principal principal,
1636                        krb5_keyblock *keyblocks,
1637                        int n_keys)
1638 {
1639     return
1640         kadm5_setkey_principal_3(server_handle, principal,
1641                                  FALSE, 0, NULL,
1642                                  keyblocks, n_keys);
1643 }
1644
1645 kadm5_ret_t
1646 kadm5_setkey_principal_3(void *server_handle,
1647                          krb5_principal principal,
1648                          krb5_boolean keepold,
1649                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1650                          krb5_keyblock *keyblocks,
1651                          int n_keys)
1652 {
1653     krb5_db_entry               kdb;
1654     osa_princ_ent_rec           adb;
1655     krb5_int32                  now;
1656     kadm5_policy_ent_rec        pol;
1657     krb5_key_data               *old_key_data;
1658     int                         n_old_keys;
1659     int                         i, j, kvno, ret, have_pol = 0;
1660 #if 0
1661     int                         last_pwd;
1662 #endif
1663     kadm5_server_handle_t       handle = server_handle;
1664     krb5_boolean                similar;
1665     krb5_keysalt                keysalt;
1666
1667     CHECK_HANDLE(server_handle);
1668
1669     if (principal == NULL || keyblocks == NULL)
1670         return EINVAL;
1671     if (hist_princ && /* this will be NULL when initializing the databse */
1672         ((krb5_principal_compare(handle->context,
1673                                  principal, hist_princ)) == TRUE))
1674         return KADM5_PROTECT_PRINCIPAL;
1675
1676     for (i = 0; i < n_keys; i++) {
1677         for (j = i+1; j < n_keys; j++) {
1678             if ((ret = krb5_c_enctype_compare(handle->context,
1679                                               keyblocks[i].enctype,
1680                                               keyblocks[j].enctype,
1681                                               &similar)))
1682                 return(ret);
1683             if (similar) {
1684                 if (n_ks_tuple) {
1685                     if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1686                         return KADM5_SETKEY_DUP_ENCTYPES;
1687                 } else
1688                     return KADM5_SETKEY_DUP_ENCTYPES;
1689             }
1690         }
1691     }
1692
1693     if (n_ks_tuple && n_ks_tuple != n_keys)
1694         return KADM5_SETKEY3_ETYPE_MISMATCH;
1695
1696     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1697        return(ret);
1698     
1699     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1700          if (kdb.key_data[i].key_data_kvno > kvno)
1701               kvno = kdb.key_data[i].key_data_kvno;
1702
1703     if (keepold) {
1704         old_key_data = kdb.key_data;
1705         n_old_keys = kdb.n_key_data;
1706     } else {
1707         if (kdb.key_data != NULL)
1708             cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1709         n_old_keys = 0;
1710         old_key_data = NULL;
1711     }
1712     
1713     kdb.key_data = (krb5_key_data*)malloc((n_keys+n_old_keys)
1714                                           *sizeof(krb5_key_data));
1715     if (kdb.key_data == NULL)
1716          return ENOMEM;
1717     memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1718     kdb.n_key_data = 0;
1719
1720     for (i = 0; i < n_keys; i++) {
1721         if (n_ks_tuple) {
1722             keysalt.type = ks_tuple[i].ks_salttype;
1723             keysalt.data.length = 0;
1724             keysalt.data.data = NULL;
1725             if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1726                 cleanup_key_data(handle->context, kdb.n_key_data,
1727                                  kdb.key_data);
1728                 return KADM5_SETKEY3_ETYPE_MISMATCH;
1729             }
1730         }
1731         ret = krb5_dbekd_encrypt_key_data(handle->context,
1732                                           &master_keyblock,
1733                                           &keyblocks[i],
1734                                           n_ks_tuple ? &keysalt : NULL,
1735                                           kvno + 1,
1736                                           &kdb.key_data[i]);
1737         if (ret) {
1738             cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1739             return ret;
1740         }
1741         kdb.n_key_data++;
1742     }
1743
1744     /* copy old key data if necessary */
1745     for (i = 0; i < n_old_keys; i++) {
1746         kdb.key_data[i+n_keys] = old_key_data[i];
1747         memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1748         kdb.n_key_data++;
1749     }
1750     /* assert(kdb.n_key_data == n_keys + n_old_keys) */
1751     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1752
1753     if ((ret = krb5_timeofday(handle->context, &now)))
1754         goto done;
1755
1756     if ((adb.aux_attributes & KADM5_POLICY)) {
1757         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1758                                     &pol)) != KADM5_OK) 
1759            goto done;
1760         have_pol = 1;
1761
1762 #if 0
1763         /*
1764           * The spec says this check is overridden if the caller has
1765           * modify privilege.  The admin server therefore makes this
1766           * check itself (in chpass_principal_wrapper, misc.c).  A
1767           * local caller implicitly has all authorization bits.
1768           */
1769         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1770                                                   &kdb, &last_pwd))
1771              goto done;
1772         if((now - last_pwd) < pol.pw_min_life &&
1773            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1774              ret = KADM5_PASS_TOOSOON;
1775              goto done;
1776         }
1777 #endif
1778 #if 0
1779         /*
1780          * Should we be checking/updating pw history here?
1781          */
1782         if(pol.pw_history_num > 1) {
1783             if(adb.admin_history_kvno != hist_kvno) {
1784                 ret = KADM5_BAD_HIST_KEY;
1785                 goto done;
1786             }
1787
1788             if (ret = check_pw_reuse(handle->context,
1789                                      &hist_key,
1790                                      kdb.n_key_data, kdb.key_data,
1791                                      adb.old_key_len, adb.old_keys))
1792                 goto done;
1793         }
1794 #endif
1795         
1796         if (pol.pw_max_life)
1797            kdb.pw_expiration = now + pol.pw_max_life;
1798         else
1799            kdb.pw_expiration = 0;
1800     } else {
1801         kdb.pw_expiration = 0;
1802     }
1803
1804     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
1805         goto done;
1806
1807     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1808         goto done;
1809
1810     ret = KADM5_OK;
1811 done:
1812     kdb_free_entry(handle, &kdb, &adb);
1813     if (have_pol)
1814          kadm5_free_policy_ent(handle->lhandle, &pol);
1815
1816     return ret;
1817 }
1818
1819 /*
1820  * Allocate an array of n_key_data krb5_keyblocks, fill in each
1821  * element with the results of decrypting the nth key in key_data with
1822  * master_keyblock, and if n_keys is not NULL fill it in with the
1823  * number of keys decrypted.
1824  */
1825 static int decrypt_key_data(krb5_context context,
1826                             int n_key_data, krb5_key_data *key_data,
1827                             krb5_keyblock **keyblocks, int *n_keys)
1828 {
1829      krb5_keyblock *keys;
1830      int ret, i;
1831
1832      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1833      if (keys == NULL)
1834           return ENOMEM;
1835      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1836
1837      for (i = 0; i < n_key_data; i++) {
1838           ret = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
1839                                             &key_data[i], 
1840                                             &keys[i], NULL);
1841           if (ret) {
1842
1843                memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1844                free(keys);
1845                return ret;
1846           }
1847      }
1848
1849      *keyblocks = keys;
1850      if (n_keys)
1851           *n_keys = n_key_data;
1852
1853      return 0;
1854 }
1855
1856 /*
1857  * Function: kadm5_decrypt_key
1858  *
1859  * Purpose: Retrieves and decrypts a principal key.
1860  *
1861  * Arguments:
1862  *
1863  *      server_handle   (r) kadm5 handle
1864  *      entry           (r) principal retrieved with kadm5_get_principal
1865  *      ktype           (r) enctype to search for, or -1 to ignore
1866  *      stype           (r) salt type to search for, or -1 to ignore
1867  *      kvno            (r) kvno to search for, -1 for max, 0 for max
1868  *                      only if it also matches ktype and stype
1869  *      keyblock        (w) keyblock to fill in
1870  *      keysalt         (w) keysalt to fill in, or NULL
1871  *      kvnop           (w) kvno to fill in, or NULL
1872  *
1873  * Effects: Searches the key_data array of entry, which must have been
1874  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1875  * find a key with a specified enctype, salt type, and kvno in a
1876  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
1877  * it with the master key, and return the key in keyblock, the salt
1878  * in salttype, and the key version number in kvno.
1879  *
1880  * If ktype or stype is -1, it is ignored for the search.  If kvno is
1881  * -1, ktype and stype are ignored and the key with the max kvno is
1882  * returned.  If kvno is 0, only the key with the max kvno is returned
1883  * and only if it matches the ktype and stype; otherwise, ENOENT is
1884  * returned.
1885  */
1886 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1887                               kadm5_principal_ent_t entry, krb5_int32
1888                               ktype, krb5_int32 stype, krb5_int32
1889                               kvno, krb5_keyblock *keyblock,
1890                               krb5_keysalt *keysalt, int *kvnop)
1891 {
1892     kadm5_server_handle_t handle = server_handle;
1893     krb5_db_entry dbent;
1894     krb5_key_data *key_data;
1895     int ret;
1896
1897     CHECK_HANDLE(server_handle);
1898
1899     if (entry->n_key_data == 0 || entry->key_data == NULL)
1900          return EINVAL;
1901
1902     /* find_enctype only uses these two fields */
1903     dbent.n_key_data = entry->n_key_data;
1904     dbent.key_data = entry->key_data;
1905     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1906                                     stype, kvno, &key_data)))
1907          return ret;
1908
1909     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
1910                                            &master_keyblock, key_data,
1911                                            keyblock, keysalt)))
1912          return ret;
1913
1914     /*
1915      * Coerce the enctype of the output keyblock in case we got an
1916      * inexact match on the enctype; this behavior will go away when
1917      * the key storage architecture gets redesigned for 1.3.
1918      */
1919     keyblock->enctype = ktype;
1920
1921     if (kvnop)
1922          *kvnop = key_data->key_data_kvno;
1923
1924     return KADM5_OK;
1925 }