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