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