00541dff16b39497e45d8b0855bc823e1d105475
[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_keyblocks,
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, *kb;
971     krb5_key_data *key_data;
972     krb5_error_code ret;
973
974     assert (n_new_key_data >= 0);
975     for (x = 0; x < (unsigned) n_new_key_data; x++) {
976         /* Check only entries with the most recent kvno. */
977         if (new_key_data[x].key_data_kvno != new_key_data[0].key_data_kvno)
978             break;
979         ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]),
980                                         &newkey, NULL);
981         if (ret)
982             return(ret);
983         for (y = 0; y < n_pw_hist_data; y++) {
984             for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) {
985                 for (kb = hist_keyblocks; kb->enctype != 0; kb++) {
986                     key_data = &pw_hist_data[y].key_data[z];
987                     ret = krb5_dbe_decrypt_key_data(context, kb, key_data,
988                                                     &histkey, NULL);
989                     if (ret)
990                         continue;
991                     if (newkey.length == histkey.length &&
992                         newkey.enctype == histkey.enctype &&
993                         memcmp(newkey.contents, histkey.contents,
994                                histkey.length) == 0) {
995                         krb5_free_keyblock_contents(context, &histkey);
996                         krb5_free_keyblock_contents(context, &newkey);
997                         return KADM5_PASS_REUSE;
998                     }
999                     krb5_free_keyblock_contents(context, &histkey);
1000                 }
1001             }
1002         }
1003         krb5_free_keyblock_contents(context, &newkey);
1004     }
1005
1006     return(0);
1007 }
1008
1009 /*
1010  * Function: create_history_entry
1011  *
1012  * Purpose: Creates a password history entry from an array of
1013  * key_data.
1014  *
1015  * Arguments:
1016  *
1017  *      context         (r) krb5_context to use
1018  *      mkey            (r) master keyblock to decrypt key data with
1019  *      hist_key        (r) history keyblock to encrypt key data with
1020  *      n_key_data      (r) number of elements in key_data
1021  *      key_data        (r) keys to add to the history entry
1022  *      hist            (w) history entry to fill in
1023  *
1024  * Effects:
1025  *
1026  * hist->key_data is allocated to store n_key_data key_datas.  Each
1027  * element of key_data is decrypted with master_keyblock, re-encrypted
1028  * in hist_key, and added to hist->key_data.  hist->n_key_data is
1029  * set to n_key_data.
1030  */
1031 static
1032 int create_history_entry(krb5_context context,
1033                          krb5_keyblock *hist_key, int n_key_data,
1034                          krb5_key_data *key_data, osa_pw_hist_ent *hist)
1035 {
1036     int i, ret;
1037     krb5_keyblock key;
1038     krb5_keysalt salt;
1039
1040     hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
1041     if (hist->key_data == NULL)
1042         return ENOMEM;
1043     memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
1044
1045     for (i = 0; i < n_key_data; i++) {
1046         ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &key,
1047                                         &salt);
1048         if (ret)
1049             return ret;
1050
1051         ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
1052                                         key_data[i].key_data_kvno,
1053                                         &hist->key_data[i]);
1054         if (ret)
1055             return ret;
1056
1057         krb5_free_keyblock_contents(context, &key);
1058         /* krb5_free_keysalt(context, &salt); */
1059     }
1060
1061     hist->n_key_data = n_key_data;
1062     return 0;
1063 }
1064
1065 static
1066 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
1067 {
1068     int i;
1069
1070     for (i = 0; i < hist->n_key_data; i++)
1071         krb5_free_key_data_contents(context, &hist->key_data[i]);
1072     free(hist->key_data);
1073 }
1074
1075 /*
1076  * Function: add_to_history
1077  *
1078  * Purpose: Adds a password to a principal's password history.
1079  *
1080  * Arguments:
1081  *
1082  *      context         (r) krb5_context to use
1083  *      hist_kvno       (r) kvno of current history key
1084  *      adb             (r/w) admin principal entry to add keys to
1085  *      pol             (r) adb's policy
1086  *      pw              (r) keys for the password to add to adb's key history
1087  *
1088  * Effects:
1089  *
1090  * add_to_history adds a single password to adb's password history.
1091  * pw contains n_key_data keys in its key_data, in storage should be
1092  * allocated but not freed by the caller (XXX blech!).
1093  *
1094  * This function maintains adb->old_keys as a circular queue.  It
1095  * starts empty, and grows each time this function is called until it
1096  * is pol->pw_history_num items long.  adb->old_key_len holds the
1097  * number of allocated entries in the array, and must therefore be [0,
1098  * pol->pw_history_num).  adb->old_key_next is the index into the
1099  * array where the next element should be written, and must be [0,
1100  * adb->old_key_len).
1101  */
1102 static kadm5_ret_t add_to_history(krb5_context context,
1103                                   krb5_kvno hist_kvno,
1104                                   osa_princ_ent_t adb,
1105                                   kadm5_policy_ent_t pol,
1106                                   osa_pw_hist_ent *pw)
1107 {
1108     osa_pw_hist_ent *histp;
1109     uint32_t nhist;
1110     unsigned int i, knext, nkeys;
1111
1112     nhist = pol->pw_history_num;
1113     /* A history of 1 means just check the current password */
1114     if (nhist <= 1)
1115         return 0;
1116
1117     if (adb->admin_history_kvno != hist_kvno) {
1118         /* The history key has changed since the last password change, so we
1119          * have to reset the password history. */
1120         free(adb->old_keys);
1121         adb->old_keys = NULL;
1122         adb->old_key_len = 0;
1123         adb->old_key_next = 0;
1124         adb->admin_history_kvno = hist_kvno;
1125     }
1126
1127     nkeys = adb->old_key_len;
1128     knext = adb->old_key_next;
1129     /* resize the adb->old_keys array if necessary */
1130     if (nkeys + 1 < nhist) {
1131         if (adb->old_keys == NULL) {
1132             adb->old_keys = (osa_pw_hist_ent *)
1133                 malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
1134         } else {
1135             adb->old_keys = (osa_pw_hist_ent *)
1136                 realloc(adb->old_keys,
1137                         (nkeys + 1) * sizeof (osa_pw_hist_ent));
1138         }
1139         if (adb->old_keys == NULL)
1140             return(ENOMEM);
1141
1142         memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
1143         nkeys = ++adb->old_key_len;
1144         /*
1145          * To avoid losing old keys, shift forward each entry after
1146          * knext.
1147          */
1148         for (i = nkeys - 1; i > knext; i--) {
1149             adb->old_keys[i] = adb->old_keys[i - 1];
1150         }
1151         memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
1152     } else if (nkeys + 1 > nhist) {
1153         /*
1154          * The policy must have changed!  Shrink the array.
1155          * Can't simply realloc() down, since it might be wrapped.
1156          * To understand the arithmetic below, note that we are
1157          * copying into new positions 0 .. N-1 from old positions
1158          * old_key_next-N .. old_key_next-1, modulo old_key_len,
1159          * where N = pw_history_num - 1 is the length of the
1160          * shortened list.        Matt Crawford, FNAL
1161          */
1162         /*
1163          * M = adb->old_key_len, N = pol->pw_history_num - 1
1164          *
1165          * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
1166          */
1167         int j;
1168         osa_pw_hist_t tmp;
1169
1170         tmp = (osa_pw_hist_ent *)
1171             malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
1172         if (tmp == NULL)
1173             return ENOMEM;
1174         for (i = 0; i < nhist - 1; i++) {
1175             /*
1176              * Add nkeys once before taking remainder to avoid
1177              * negative values.
1178              */
1179             j = (i + nkeys + knext - (nhist - 1)) % nkeys;
1180             tmp[i] = adb->old_keys[j];
1181         }
1182         /* Now free the ones we don't keep (the oldest ones) */
1183         for (i = 0; i < nkeys - (nhist - 1); i++) {
1184             j = (i + nkeys + knext) % nkeys;
1185             histp = &adb->old_keys[j];
1186             for (j = 0; j < histp->n_key_data; j++) {
1187                 krb5_free_key_data_contents(context, &histp->key_data[j]);
1188             }
1189             free(histp->key_data);
1190         }
1191         free(adb->old_keys);
1192         adb->old_keys = tmp;
1193         nkeys = adb->old_key_len = nhist - 1;
1194         knext = adb->old_key_next = 0;
1195     }
1196
1197     /*
1198      * If nhist decreased since the last password change, and nkeys+1
1199      * is less than the previous nhist, it is possible for knext to
1200      * index into unallocated space.  This condition would not be
1201      * caught by the resizing code above.
1202      */
1203     if (knext + 1 > nkeys)
1204         knext = adb->old_key_next = 0;
1205     /* free the old pw history entry if it contains data */
1206     histp = &adb->old_keys[knext];
1207     for (i = 0; i < (unsigned int) histp->n_key_data; i++)
1208         krb5_free_key_data_contents(context, &histp->key_data[i]);
1209     free(histp->key_data);
1210
1211     /* store the new entry */
1212     adb->old_keys[knext] = *pw;
1213
1214     /* update the next pointer */
1215     if (++adb->old_key_next == nhist - 1)
1216         adb->old_key_next = 0;
1217
1218     return(0);
1219 }
1220
1221 /* FIXME: don't use global variable for this */
1222 krb5_boolean use_password_server = 0;
1223
1224 #ifdef USE_PASSWORD_SERVER
1225 static krb5_boolean
1226 kadm5_use_password_server (void)
1227 {
1228     return use_password_server;
1229 }
1230 #endif
1231
1232 void
1233 kadm5_set_use_password_server (void)
1234 {
1235     use_password_server = 1;
1236 }
1237
1238 #ifdef USE_PASSWORD_SERVER
1239
1240 /*
1241  * kadm5_launch_task () runs a program (task_path) to synchronize the
1242  * Apple password server with the Kerberos database.  Password server
1243  * programs can receive arguments on the command line (task_argv)
1244  * and a block of data via stdin (data_buffer).
1245  *
1246  * Because a failure to communicate with the tool results in the
1247  * password server falling out of sync with the database,
1248  * kadm5_launch_task() always fails if it can't talk to the tool.
1249  */
1250
1251 static kadm5_ret_t
1252 kadm5_launch_task (krb5_context context,
1253                    const char *task_path, char * const task_argv[],
1254                    const char *buffer)
1255 {
1256     kadm5_ret_t ret;
1257     int data_pipe[2];
1258
1259     ret = pipe (data_pipe);
1260     if (ret)
1261         ret = errno;
1262
1263     if (!ret) {
1264         pid_t pid = fork ();
1265         if (pid == -1) {
1266             ret = errno;
1267             close (data_pipe[0]);
1268             close (data_pipe[1]);
1269         } else if (pid == 0) {
1270             /* The child: */
1271
1272             if (dup2 (data_pipe[0], STDIN_FILENO) == -1)
1273                 _exit (1);
1274
1275             close (data_pipe[0]);
1276             close (data_pipe[1]);
1277
1278             execv (task_path, task_argv);
1279
1280             _exit (1); /* Fail if execv fails */
1281         } else {
1282             /* The parent: */
1283             int status;
1284
1285             ret = 0;
1286
1287             close (data_pipe[0]);
1288
1289             /* Write out the buffer to the child, add \n */
1290             if (buffer) {
1291                 if (krb5_net_write (context, data_pipe[1], buffer, strlen (buffer)) < 0
1292                     || krb5_net_write (context, data_pipe[1], "\n", 1) < 0)
1293                 {
1294                     /* kill the child to make sure waitpid() won't hang later */
1295                     ret = errno;
1296                     kill (pid, SIGKILL);
1297                 }
1298             }
1299             close (data_pipe[1]);
1300
1301             waitpid (pid, &status, 0);
1302
1303             if (!ret) {
1304                 if (WIFEXITED (status)) {
1305                     /* child read password and exited.  Check the return value. */
1306                     if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
1307                         ret = KRB5KDC_ERR_POLICY; /* password change rejected */
1308                     }
1309                 } else {
1310                     /* child read password but crashed or was killed */
1311                     ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1312                 }
1313             }
1314         }
1315     }
1316
1317     return ret;
1318 }
1319
1320 #endif
1321
1322 kadm5_ret_t
1323 kadm5_chpass_principal(void *server_handle,
1324                        krb5_principal principal, char *password)
1325 {
1326     return
1327         kadm5_chpass_principal_3(server_handle, principal, FALSE,
1328                                  0, NULL, password);
1329 }
1330
1331 kadm5_ret_t
1332 kadm5_chpass_principal_3(void *server_handle,
1333                          krb5_principal principal, krb5_boolean keepold,
1334                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1335                          char *password)
1336 {
1337     krb5_int32                  now;
1338     kadm5_policy_ent_rec        pol;
1339     osa_princ_ent_rec           adb;
1340     krb5_db_entry               *kdb;
1341     int                         ret, ret2, last_pwd, hist_added;
1342     int                         have_pol = 0;
1343     kadm5_server_handle_t       handle = server_handle;
1344     osa_pw_hist_ent             hist;
1345     krb5_keyblock               *act_mkey, *hist_keyblocks = NULL;
1346     krb5_kvno                   act_kvno, hist_kvno;
1347
1348     CHECK_HANDLE(server_handle);
1349
1350     krb5_clear_error_message(handle->context);
1351
1352     hist_added = 0;
1353     memset(&hist, 0, sizeof(hist));
1354
1355     if (principal == NULL || password == NULL)
1356         return EINVAL;
1357     if ((krb5_principal_compare(handle->context,
1358                                 principal, hist_princ)) == TRUE)
1359         return KADM5_PROTECT_PRINCIPAL;
1360
1361     /* Use default keysalts if caller did not provide any. */
1362     if (n_ks_tuple == 0) {
1363         ks_tuple = handle->params.keysalts;
1364         n_ks_tuple = handle->params.num_keysalts;
1365     }
1366
1367     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1368         return(ret);
1369
1370     if ((adb.aux_attributes & KADM5_POLICY)) {
1371         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1372             goto done;
1373         have_pol = 1;
1374
1375         /* Create a password history entry before we change kdb's key_data. */
1376         ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
1377         if (ret)
1378             goto done;
1379         ret = create_history_entry(handle->context, &hist_keyblocks[0],
1380                                    kdb->n_key_data, kdb->key_data, &hist);
1381         if (ret)
1382             goto done;
1383     }
1384
1385     if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
1386                             principal)))
1387         goto done;
1388
1389     ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno,
1390                                  &act_mkey);
1391     if (ret)
1392         goto done;
1393
1394     ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
1395                        password, 0 /* increment kvno */,
1396                        keepold, kdb);
1397     if (ret)
1398         goto done;
1399
1400     ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
1401     if (ret)
1402         goto done;
1403
1404     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1405
1406     ret = krb5_timeofday(handle->context, &now);
1407     if (ret)
1408         goto done;
1409
1410     if ((adb.aux_attributes & KADM5_POLICY)) {
1411         /* the policy was loaded before */
1412
1413         ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
1414         if (ret)
1415             goto done;
1416
1417 #if 0
1418         /*
1419          * The spec says this check is overridden if the caller has
1420          * modify privilege.  The admin server therefore makes this
1421          * check itself (in chpass_principal_wrapper, misc.c). A
1422          * local caller implicitly has all authorization bits.
1423          */
1424         if ((now - last_pwd) < pol.pw_min_life &&
1425             !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1426             ret = KADM5_PASS_TOOSOON;
1427             goto done;
1428         }
1429 #endif
1430
1431         ret = check_pw_reuse(handle->context, hist_keyblocks,
1432                              kdb->n_key_data, kdb->key_data,
1433                              1, &hist);
1434         if (ret)
1435             goto done;
1436
1437         if (pol.pw_history_num > 1) {
1438             /* If hist_kvno has changed since the last password change, we
1439              * can't check the history. */
1440             if (adb.admin_history_kvno == hist_kvno) {
1441                 ret = check_pw_reuse(handle->context, hist_keyblocks,
1442                                      kdb->n_key_data, kdb->key_data,
1443                                      adb.old_key_len, adb.old_keys);
1444                 if (ret)
1445                     goto done;
1446             }
1447
1448             ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
1449                                  &hist);
1450             if (ret)
1451                 goto done;
1452             hist_added = 1;
1453         }
1454
1455         if (pol.pw_max_life)
1456             kdb->pw_expiration = now + pol.pw_max_life;
1457         else
1458             kdb->pw_expiration = 0;
1459     } else {
1460         kdb->pw_expiration = 0;
1461     }
1462
1463 #ifdef USE_PASSWORD_SERVER
1464     if (kadm5_use_password_server () &&
1465         (krb5_princ_size (handle->context, principal) == 1)) {
1466         krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
1467         const char *path = "/usr/sbin/mkpassdb";
1468         char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
1469         char *pstring = NULL;
1470
1471         if (!ret) {
1472             pstring = malloc ((princ->length + 1) * sizeof (char));
1473             if (pstring == NULL) { ret = ENOMEM; }
1474         }
1475
1476         if (!ret) {
1477             memcpy (pstring, princ->data, princ->length);
1478             pstring [princ->length] = '\0';
1479             argv[2] = pstring;
1480
1481             ret = kadm5_launch_task (handle->context, path, argv, password);
1482         }
1483
1484         if (pstring != NULL)
1485             free (pstring);
1486
1487         if (ret)
1488             goto done;
1489     }
1490 #endif
1491
1492     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1493     if (ret)
1494         goto done;
1495
1496     /* unlock principal on this KDC */
1497     kdb->fail_auth_count = 0;
1498
1499     /* key data and attributes changed, let the database provider know */
1500     kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES |
1501         KADM5_FAIL_AUTH_COUNT;
1502     /* | KADM5_CPW_FUNCTION */
1503
1504     ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1505                                KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1506                                n_ks_tuple, ks_tuple, password);
1507     if (ret)
1508         goto done;
1509
1510     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1511         goto done;
1512
1513     (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1514                                 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1515                                 keepold, n_ks_tuple, ks_tuple, password);
1516     ret = KADM5_OK;
1517 done:
1518     if (!hist_added && hist.key_data)
1519         free_history_entry(handle->context, &hist);
1520     kdb_free_entry(handle, kdb, &adb);
1521     kdb_free_keyblocks(handle, hist_keyblocks);
1522
1523     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1524         && !ret)
1525         ret = ret2;
1526
1527     return ret;
1528 }
1529
1530 kadm5_ret_t
1531 kadm5_randkey_principal(void *server_handle,
1532                         krb5_principal principal,
1533                         krb5_keyblock **keyblocks,
1534                         int *n_keys)
1535 {
1536     return
1537         kadm5_randkey_principal_3(server_handle, principal,
1538                                   FALSE, 0, NULL,
1539                                   keyblocks, n_keys);
1540 }
1541 kadm5_ret_t
1542 kadm5_randkey_principal_3(void *server_handle,
1543                           krb5_principal principal,
1544                           krb5_boolean keepold,
1545                           int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1546                           krb5_keyblock **keyblocks,
1547                           int *n_keys)
1548 {
1549     krb5_db_entry               *kdb;
1550     osa_princ_ent_rec           adb;
1551     krb5_int32                  now;
1552     kadm5_policy_ent_rec        pol;
1553     int                         ret, last_pwd, have_pol = 0;
1554     kadm5_server_handle_t       handle = server_handle;
1555     krb5_keyblock               *act_mkey;
1556
1557     if (keyblocks)
1558         *keyblocks = NULL;
1559
1560     CHECK_HANDLE(server_handle);
1561
1562     /* Use default keysalts if caller did not provide any. */
1563     if (n_ks_tuple == 0) {
1564         ks_tuple = handle->params.keysalts;
1565         n_ks_tuple = handle->params.num_keysalts;
1566     }
1567
1568     krb5_clear_error_message(handle->context);
1569
1570     if (principal == NULL)
1571         return EINVAL;
1572     if (krb5_principal_compare(handle->context, principal, hist_princ)) {
1573         /* If changing the history entry, the new entry must have exactly one
1574          * key. */
1575         if (keepold)
1576             return KADM5_PROTECT_PRINCIPAL;
1577         n_ks_tuple = 1;
1578     }
1579
1580     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1581         return(ret);
1582
1583     ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
1584                                  &act_mkey);
1585     if (ret)
1586         goto done;
1587
1588     ret = krb5_dbe_crk(handle->context, act_mkey, ks_tuple, n_ks_tuple,
1589                        keepold, kdb);
1590     if (ret)
1591         goto done;
1592
1593     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1594
1595     ret = krb5_timeofday(handle->context, &now);
1596     if (ret)
1597         goto done;
1598
1599     if ((adb.aux_attributes & KADM5_POLICY)) {
1600         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1601                                     &pol)) != KADM5_OK)
1602             goto done;
1603         have_pol = 1;
1604
1605         ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
1606         if (ret)
1607             goto done;
1608
1609 #if 0
1610         /*
1611          * The spec says this check is overridden if the caller has
1612          * modify privilege.  The admin server therefore makes this
1613          * check itself (in chpass_principal_wrapper, misc.c).  A
1614          * local caller implicitly has all authorization bits.
1615          */
1616         if((now - last_pwd) < pol.pw_min_life &&
1617            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1618             ret = KADM5_PASS_TOOSOON;
1619             goto done;
1620         }
1621 #endif
1622
1623         if (pol.pw_max_life)
1624             kdb->pw_expiration = now + pol.pw_max_life;
1625         else
1626             kdb->pw_expiration = 0;
1627     } else {
1628         kdb->pw_expiration = 0;
1629     }
1630
1631     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1632     if (ret)
1633         goto done;
1634
1635     /* unlock principal on this KDC */
1636     kdb->fail_auth_count = 0;
1637
1638     if (keyblocks) {
1639         ret = decrypt_key_data(handle->context,
1640                                kdb->n_key_data, kdb->key_data,
1641                                keyblocks, n_keys);
1642         if (ret)
1643             goto done;
1644     }
1645
1646     /* key data changed, let the database provider know */
1647     kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT;
1648     /* | KADM5_RANDKEY_USED */;
1649
1650     ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1651                                KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1652                                n_ks_tuple, ks_tuple, NULL);
1653     if (ret)
1654         goto done;
1655     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1656         goto done;
1657
1658     (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1659                                 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1660                                 keepold, n_ks_tuple, ks_tuple, NULL);
1661     ret = KADM5_OK;
1662 done:
1663     kdb_free_entry(handle, kdb, &adb);
1664     if (have_pol)
1665         kadm5_free_policy_ent(handle->lhandle, &pol);
1666
1667     return ret;
1668 }
1669
1670 /*
1671  * kadm5_setv4key_principal:
1672  *
1673  * Set only ONE key of the principal, removing all others.  This key
1674  * must have the DES_CBC_CRC enctype and is entered as having the
1675  * krb4 salttype.  This is to enable things like kadmind4 to work.
1676  */
1677 kadm5_ret_t
1678 kadm5_setv4key_principal(void *server_handle,
1679                          krb5_principal principal,
1680                          krb5_keyblock *keyblock)
1681 {
1682     krb5_db_entry               *kdb;
1683     osa_princ_ent_rec           adb;
1684     krb5_int32                  now;
1685     kadm5_policy_ent_rec        pol;
1686     krb5_keysalt                keysalt;
1687     int                         i, k, kvno, ret, have_pol = 0;
1688 #if 0
1689     int                         last_pwd;
1690 #endif
1691     kadm5_server_handle_t       handle = server_handle;
1692     krb5_key_data               tmp_key_data;
1693     krb5_keyblock               *act_mkey;
1694
1695     memset( &tmp_key_data, 0, sizeof(tmp_key_data));
1696
1697     CHECK_HANDLE(server_handle);
1698
1699     krb5_clear_error_message(handle->context);
1700
1701     if (principal == NULL || keyblock == NULL)
1702         return EINVAL;
1703     if (hist_princ && /* this will be NULL when initializing the databse */
1704         ((krb5_principal_compare(handle->context,
1705                                  principal, hist_princ)) == TRUE))
1706         return KADM5_PROTECT_PRINCIPAL;
1707
1708     if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1709         return KADM5_SETV4KEY_INVAL_ENCTYPE;
1710
1711     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1712         return(ret);
1713
1714     for (kvno = 0, i=0; i<kdb->n_key_data; i++)
1715         if (kdb->key_data[i].key_data_kvno > kvno)
1716             kvno = kdb->key_data[i].key_data_kvno;
1717
1718     if (kdb->key_data != NULL)
1719         cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1720
1721     kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
1722     if (kdb->key_data == NULL)
1723         return ENOMEM;
1724     memset(kdb->key_data, 0, sizeof(krb5_key_data));
1725     kdb->n_key_data = 1;
1726     keysalt.type = KRB5_KDB_SALTTYPE_V4;
1727     /* XXX data.magic? */
1728     keysalt.data.length = 0;
1729     keysalt.data.data = NULL;
1730
1731     ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
1732                                  &act_mkey);
1733     if (ret)
1734         goto done;
1735
1736     /* use tmp_key_data as temporary location and reallocate later */
1737     ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, keyblock,
1738                                     &keysalt, kvno + 1, &tmp_key_data);
1739     if (ret) {
1740         goto done;
1741     }
1742
1743     for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1744         kdb->key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
1745         kdb->key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
1746         if (tmp_key_data.key_data_contents[k]) {
1747             kdb->key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1748             if (kdb->key_data->key_data_contents[k] == NULL) {
1749                 cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1750                 kdb->key_data = NULL;
1751                 kdb->n_key_data = 0;
1752                 ret = ENOMEM;
1753                 goto done;
1754             }
1755             memcpy (kdb->key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1756
1757             memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1758             free (tmp_key_data.key_data_contents[k]);
1759             tmp_key_data.key_data_contents[k] = NULL;
1760         }
1761     }
1762
1763
1764
1765     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1766
1767     ret = krb5_timeofday(handle->context, &now);
1768     if (ret)
1769         goto done;
1770
1771     if ((adb.aux_attributes & KADM5_POLICY)) {
1772         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1773                                     &pol)) != KADM5_OK)
1774             goto done;
1775         have_pol = 1;
1776
1777 #if 0
1778         /*
1779          * The spec says this check is overridden if the caller has
1780          * modify privilege.  The admin server therefore makes this
1781          * check itself (in chpass_principal_wrapper, misc.c).  A
1782          * local caller implicitly has all authorization bits.
1783          */
1784         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1785                                                   kdb, &last_pwd))
1786             goto done;
1787         if((now - last_pwd) < pol.pw_min_life &&
1788            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1789             ret = KADM5_PASS_TOOSOON;
1790             goto done;
1791         }
1792 #endif
1793
1794         if (pol.pw_max_life)
1795             kdb->pw_expiration = now + pol.pw_max_life;
1796         else
1797             kdb->pw_expiration = 0;
1798     } else {
1799         kdb->pw_expiration = 0;
1800     }
1801
1802     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1803     if (ret)
1804         goto done;
1805
1806     /* unlock principal on this KDC */
1807     kdb->fail_auth_count = 0;
1808
1809     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1810         goto done;
1811
1812     ret = KADM5_OK;
1813 done:
1814     for (i = 0; i < tmp_key_data.key_data_ver; i++) {
1815         if (tmp_key_data.key_data_contents[i]) {
1816             memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
1817             free (tmp_key_data.key_data_contents[i]);
1818         }
1819     }
1820
1821     kdb_free_entry(handle, kdb, &adb);
1822     if (have_pol)
1823         kadm5_free_policy_ent(handle->lhandle, &pol);
1824
1825     return ret;
1826 }
1827
1828 kadm5_ret_t
1829 kadm5_setkey_principal(void *server_handle,
1830                        krb5_principal principal,
1831                        krb5_keyblock *keyblocks,
1832                        int n_keys)
1833 {
1834     return
1835         kadm5_setkey_principal_3(server_handle, principal,
1836                                  FALSE, 0, NULL,
1837                                  keyblocks, n_keys);
1838 }
1839
1840 kadm5_ret_t
1841 kadm5_setkey_principal_3(void *server_handle,
1842                          krb5_principal principal,
1843                          krb5_boolean keepold,
1844                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1845                          krb5_keyblock *keyblocks,
1846                          int n_keys)
1847 {
1848     krb5_db_entry               *kdb;
1849     osa_princ_ent_rec           adb;
1850     krb5_int32                  now;
1851     kadm5_policy_ent_rec        pol;
1852     krb5_key_data               *old_key_data;
1853     int                         n_old_keys;
1854     int                         i, j, k, kvno, ret, have_pol = 0;
1855 #if 0
1856     int                         last_pwd;
1857 #endif
1858     kadm5_server_handle_t       handle = server_handle;
1859     krb5_boolean                similar;
1860     krb5_keysalt                keysalt;
1861     krb5_key_data         tmp_key_data;
1862     krb5_key_data        *tptr;
1863     krb5_keyblock               *act_mkey;
1864
1865     CHECK_HANDLE(server_handle);
1866
1867     krb5_clear_error_message(handle->context);
1868
1869     if (principal == NULL || keyblocks == NULL)
1870         return EINVAL;
1871     if (hist_princ && /* this will be NULL when initializing the databse */
1872         ((krb5_principal_compare(handle->context,
1873                                  principal, hist_princ)) == TRUE))
1874         return KADM5_PROTECT_PRINCIPAL;
1875
1876     for (i = 0; i < n_keys; i++) {
1877         for (j = i+1; j < n_keys; j++) {
1878             if ((ret = krb5_c_enctype_compare(handle->context,
1879                                               keyblocks[i].enctype,
1880                                               keyblocks[j].enctype,
1881                                               &similar)))
1882                 return(ret);
1883             if (similar) {
1884                 if (n_ks_tuple) {
1885                     if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1886                         return KADM5_SETKEY_DUP_ENCTYPES;
1887                 } else
1888                     return KADM5_SETKEY_DUP_ENCTYPES;
1889             }
1890         }
1891     }
1892
1893     if (n_ks_tuple && n_ks_tuple != n_keys)
1894         return KADM5_SETKEY3_ETYPE_MISMATCH;
1895
1896     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1897         return(ret);
1898
1899     for (kvno = 0, i=0; i<kdb->n_key_data; i++)
1900         if (kdb->key_data[i].key_data_kvno > kvno)
1901             kvno = kdb->key_data[i].key_data_kvno;
1902
1903     if (keepold) {
1904         old_key_data = kdb->key_data;
1905         n_old_keys = kdb->n_key_data;
1906     } else {
1907         if (kdb->key_data != NULL)
1908             cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1909         n_old_keys = 0;
1910         old_key_data = NULL;
1911     }
1912
1913     kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
1914                                                   *sizeof(krb5_key_data));
1915     if (kdb->key_data == NULL) {
1916         ret = ENOMEM;
1917         goto done;
1918     }
1919
1920     memset(kdb->key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1921     kdb->n_key_data = 0;
1922
1923     for (i = 0; i < n_keys; i++) {
1924         if (n_ks_tuple) {
1925             keysalt.type = ks_tuple[i].ks_salttype;
1926             keysalt.data.length = 0;
1927             keysalt.data.data = NULL;
1928             if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1929                 ret = KADM5_SETKEY3_ETYPE_MISMATCH;
1930                 goto done;
1931             }
1932         }
1933         memset (&tmp_key_data, 0, sizeof(tmp_key_data));
1934
1935         ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
1936                                      &act_mkey);
1937         if (ret)
1938             goto done;
1939
1940         ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
1941                                         &keyblocks[i],
1942                                         n_ks_tuple ? &keysalt : NULL, kvno + 1,
1943                                         &tmp_key_data);
1944         if (ret)
1945             goto done;
1946
1947         tptr = &kdb->key_data[i];
1948         tptr->key_data_ver = tmp_key_data.key_data_ver;
1949         tptr->key_data_kvno = tmp_key_data.key_data_kvno;
1950         for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1951             tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
1952             tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
1953             if (tmp_key_data.key_data_contents[k]) {
1954                 tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1955                 if (tptr->key_data_contents[k] == NULL) {
1956                     int i1;
1957                     for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
1958                         if (tmp_key_data.key_data_contents[i1]) {
1959                             memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
1960                             free (tmp_key_data.key_data_contents[i1]);
1961                         }
1962                     }
1963
1964                     ret =  ENOMEM;
1965                     goto done;
1966                 }
1967                 memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1968
1969                 memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1970                 free (tmp_key_data.key_data_contents[k]);
1971                 tmp_key_data.key_data_contents[k] = NULL;
1972             }
1973         }
1974         kdb->n_key_data++;
1975     }
1976
1977     /* copy old key data if necessary */
1978     for (i = 0; i < n_old_keys; i++) {
1979         kdb->key_data[i+n_keys] = old_key_data[i];
1980         memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1981         kdb->n_key_data++;
1982     }
1983
1984     if (old_key_data)
1985         krb5_db_free(handle->context, old_key_data);
1986
1987     /* assert(kdb->n_key_data == n_keys + n_old_keys) */
1988     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1989
1990     if ((ret = krb5_timeofday(handle->context, &now)))
1991         goto done;
1992
1993     if ((adb.aux_attributes & KADM5_POLICY)) {
1994         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1995                                     &pol)) != KADM5_OK)
1996             goto done;
1997         have_pol = 1;
1998
1999 #if 0
2000         /*
2001          * The spec says this check is overridden if the caller has
2002          * modify privilege.  The admin server therefore makes this
2003          * check itself (in chpass_principal_wrapper, misc.c).  A
2004          * local caller implicitly has all authorization bits.
2005          */
2006         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
2007                                                   kdb, &last_pwd))
2008             goto done;
2009         if((now - last_pwd) < pol.pw_min_life &&
2010            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2011             ret = KADM5_PASS_TOOSOON;
2012             goto done;
2013         }
2014 #endif
2015
2016         if (pol.pw_max_life)
2017             kdb->pw_expiration = now + pol.pw_max_life;
2018         else
2019             kdb->pw_expiration = 0;
2020     } else {
2021         kdb->pw_expiration = 0;
2022     }
2023
2024     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
2025         goto done;
2026
2027     /* unlock principal on this KDC */
2028     kdb->fail_auth_count = 0;
2029
2030     if ((ret = kdb_put_entry(handle, kdb, &adb)))
2031         goto done;
2032
2033     ret = KADM5_OK;
2034 done:
2035     kdb_free_entry(handle, kdb, &adb);
2036     if (have_pol)
2037         kadm5_free_policy_ent(handle->lhandle, &pol);
2038
2039     return ret;
2040 }
2041
2042 /*
2043  * Return the list of keys like kadm5_randkey_principal,
2044  * but don't modify the principal.
2045  */
2046 kadm5_ret_t
2047 kadm5_get_principal_keys(void *server_handle /* IN */,
2048                          krb5_principal principal /* IN */,
2049                          krb5_keyblock **keyblocks /* OUT */,
2050                          int *n_keys /* OUT */)
2051 {
2052     krb5_db_entry               *kdb;
2053     osa_princ_ent_rec           adb;
2054     kadm5_ret_t                 ret;
2055     kadm5_server_handle_t       handle = server_handle;
2056
2057     if (keyblocks)
2058         *keyblocks = NULL;
2059
2060     CHECK_HANDLE(server_handle);
2061
2062     if (principal == NULL)
2063         return EINVAL;
2064
2065     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
2066         return(ret);
2067
2068     if (keyblocks) {
2069         ret = decrypt_key_data(handle->context,
2070                                kdb->n_key_data, kdb->key_data,
2071                                keyblocks, n_keys);
2072         if (ret)
2073             goto done;
2074     }
2075
2076     ret = KADM5_OK;
2077 done:
2078     kdb_free_entry(handle, kdb, &adb);
2079
2080     return ret;
2081 }
2082
2083
2084 /*
2085  * Allocate an array of n_key_data krb5_keyblocks, fill in each
2086  * element with the results of decrypting the nth key in key_data,
2087  * and if n_keys is not NULL fill it in with the
2088  * number of keys decrypted.
2089  */
2090 static int decrypt_key_data(krb5_context context,
2091                             int n_key_data, krb5_key_data *key_data,
2092                             krb5_keyblock **keyblocks, int *n_keys)
2093 {
2094     krb5_keyblock *keys;
2095     int ret, i;
2096
2097     keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
2098     if (keys == NULL)
2099         return ENOMEM;
2100     memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
2101
2102     for (i = 0; i < n_key_data; i++) {
2103         ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &keys[i],
2104                                         NULL);
2105         if (ret) {
2106             for (; i >= 0; i--) {
2107                 if (keys[i].contents) {
2108                     memset (keys[i].contents, 0, keys[i].length);
2109                     free( keys[i].contents );
2110                 }
2111             }
2112
2113             memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
2114             free(keys);
2115             return ret;
2116         }
2117     }
2118
2119     *keyblocks = keys;
2120     if (n_keys)
2121         *n_keys = n_key_data;
2122
2123     return 0;
2124 }
2125
2126 /*
2127  * Function: kadm5_decrypt_key
2128  *
2129  * Purpose: Retrieves and decrypts a principal key.
2130  *
2131  * Arguments:
2132  *
2133  *      server_handle   (r) kadm5 handle
2134  *      entry           (r) principal retrieved with kadm5_get_principal
2135  *      ktype           (r) enctype to search for, or -1 to ignore
2136  *      stype           (r) salt type to search for, or -1 to ignore
2137  *      kvno            (r) kvno to search for, -1 for max, 0 for max
2138  *                      only if it also matches ktype and stype
2139  *      keyblock        (w) keyblock to fill in
2140  *      keysalt         (w) keysalt to fill in, or NULL
2141  *      kvnop           (w) kvno to fill in, or NULL
2142  *
2143  * Effects: Searches the key_data array of entry, which must have been
2144  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2145  * find a key with a specified enctype, salt type, and kvno in a
2146  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
2147  * it with the master key, and return the key in keyblock, the salt
2148  * in salttype, and the key version number in kvno.
2149  *
2150  * If ktype or stype is -1, it is ignored for the search.  If kvno is
2151  * -1, ktype and stype are ignored and the key with the max kvno is
2152  * returned.  If kvno is 0, only the key with the max kvno is returned
2153  * and only if it matches the ktype and stype; otherwise, ENOENT is
2154  * returned.
2155  */
2156 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
2157                               kadm5_principal_ent_t entry, krb5_int32
2158                               ktype, krb5_int32 stype, krb5_int32
2159                               kvno, krb5_keyblock *keyblock,
2160                               krb5_keysalt *keysalt, int *kvnop)
2161 {
2162     kadm5_server_handle_t handle = server_handle;
2163     krb5_db_entry dbent;
2164     krb5_key_data *key_data;
2165     krb5_keyblock *mkey_ptr;
2166     int ret;
2167
2168     CHECK_HANDLE(server_handle);
2169
2170     if (entry->n_key_data == 0 || entry->key_data == NULL)
2171         return EINVAL;
2172
2173     /* find_enctype only uses these two fields */
2174     dbent.n_key_data = entry->n_key_data;
2175     dbent.key_data = entry->key_data;
2176     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
2177                                      stype, kvno, &key_data)))
2178         return ret;
2179
2180     /* find_mkey only uses this field */
2181     dbent.tl_data = entry->tl_data;
2182     if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, &mkey_ptr))) {
2183         /* try refreshing master key list */
2184         /* XXX it would nice if we had the mkvno here for optimization */
2185         if (krb5_db_fetch_mkey_list(handle->context, master_princ,
2186                                     &master_keyblock) == 0) {
2187             if ((ret = krb5_dbe_find_mkey(handle->context, &dbent,
2188                                           &mkey_ptr))) {
2189                 return ret;
2190             }
2191         } else {
2192             return ret;
2193         }
2194     }
2195
2196     if ((ret = krb5_dbe_decrypt_key_data(handle->context, NULL, key_data,
2197                                          keyblock, keysalt)))
2198         return ret;
2199
2200     /*
2201      * Coerce the enctype of the output keyblock in case we got an
2202      * inexact match on the enctype; this behavior will go away when
2203      * the key storage architecture gets redesigned for 1.3.
2204      */
2205     if (ktype != -1)
2206         keyblock->enctype = ktype;
2207
2208     if (kvnop)
2209         *kvnop = key_data->key_data_kvno;
2210
2211     return KADM5_OK;
2212 }
2213
2214 kadm5_ret_t
2215 kadm5_purgekeys(void *server_handle,
2216                 krb5_principal principal,
2217                 int keepkvno)
2218 {
2219     kadm5_server_handle_t handle = server_handle;
2220     kadm5_ret_t ret;
2221     krb5_db_entry *kdb;
2222     osa_princ_ent_rec adb;
2223     krb5_key_data *old_keydata;
2224     int n_old_keydata;
2225     int i, j, k;
2226
2227     CHECK_HANDLE(server_handle);
2228
2229     if (principal == NULL)
2230         return EINVAL;
2231
2232     ret = kdb_get_entry(handle, principal, &kdb, &adb);
2233     if (ret)
2234         return(ret);
2235
2236     if (keepkvno <= 0) {
2237         keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
2238                                              kdb->key_data);
2239     }
2240
2241     old_keydata = kdb->key_data;
2242     n_old_keydata = kdb->n_key_data;
2243     kdb->n_key_data = 0;
2244     kdb->key_data = krb5_db_alloc(handle->context, NULL,
2245                                   n_old_keydata * sizeof(krb5_key_data));
2246     if (kdb->key_data == NULL) {
2247         ret = ENOMEM;
2248         goto done;
2249     }
2250     memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
2251     for (i = 0, j = 0; i < n_old_keydata; i++) {
2252         if (old_keydata[i].key_data_kvno < keepkvno)
2253             continue;
2254
2255         /* Alias the key_data_contents pointers; we null them out in the
2256          * source array immediately after. */
2257         kdb->key_data[j] = old_keydata[i];
2258         for (k = 0; k < old_keydata[i].key_data_ver; k++) {
2259             old_keydata[i].key_data_contents[k] = NULL;
2260         }
2261         j++;
2262     }
2263     kdb->n_key_data = j;
2264     cleanup_key_data(handle->context, n_old_keydata, old_keydata);
2265
2266     kdb->mask = KADM5_KEY_DATA;
2267     ret = kdb_put_entry(handle, kdb, &adb);
2268     if (ret)
2269         goto done;
2270
2271 done:
2272     kdb_free_entry(handle, kdb, &adb);
2273     return ret;
2274 }
2275
2276 kadm5_ret_t
2277 kadm5_get_strings(void *server_handle, krb5_principal principal,
2278                   krb5_string_attr **strings_out, int *count_out)
2279 {
2280     kadm5_server_handle_t handle = server_handle;
2281     kadm5_ret_t ret;
2282     krb5_db_entry *kdb = NULL;
2283
2284     *strings_out = NULL;
2285     *count_out = 0;
2286     CHECK_HANDLE(server_handle);
2287     if (principal == NULL)
2288         return EINVAL;
2289
2290     ret = kdb_get_entry(handle, principal, &kdb, NULL);
2291     if (ret)
2292         return ret;
2293
2294     ret = krb5_dbe_get_strings(handle->context, kdb, strings_out, count_out);
2295     kdb_free_entry(handle, kdb, NULL);
2296     return ret;
2297 }
2298
2299 kadm5_ret_t
2300 kadm5_set_string(void *server_handle, krb5_principal principal,
2301                  const char *key, const char *value)
2302 {
2303     kadm5_server_handle_t handle = server_handle;
2304     kadm5_ret_t ret;
2305     krb5_db_entry *kdb;
2306     osa_princ_ent_rec adb;
2307
2308     CHECK_HANDLE(server_handle);
2309     if (principal == NULL || key == NULL)
2310         return EINVAL;
2311
2312     ret = kdb_get_entry(handle, principal, &kdb, &adb);
2313     if (ret)
2314         return ret;
2315
2316     ret = krb5_dbe_set_string(handle->context, kdb, key, value);
2317     if (ret)
2318         goto done;
2319
2320     kdb->mask = KADM5_TL_DATA;
2321     ret = kdb_put_entry(handle, kdb, &adb);
2322
2323 done:
2324     kdb_free_entry(handle, kdb, &adb);
2325     return ret;
2326 }