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