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