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