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