Allow clearpolicy restriction for kadmin addprinc
[krb5.git] / src / lib / kadm5 / srv / svr_principal.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
4  *
5  * $Header$
6  */
7 #include "k5-int.h"
8 #include        <sys/time.h>
9 #include        <kadm5/admin.h>
10 #include        <kdb.h>
11 #include        "server_internal.h"
12 #ifdef USE_PASSWORD_SERVER
13 #include        <sys/wait.h>
14 #include        <signal.h>
15 #endif
16
17 #include <krb5/kadm5_hook_plugin.h>
18
19 #ifdef USE_VALGRIND
20 #include <valgrind/memcheck.h>
21 #else
22 #define VALGRIND_CHECK_DEFINED(LVALUE) ((void)0)
23 #endif
24
25 extern  krb5_principal      master_princ;
26 extern  krb5_principal      hist_princ;
27 extern  krb5_keyblock       master_keyblock;
28 extern  krb5_actkvno_node  *active_mkey_list;
29 extern  krb5_db_entry       master_db;
30
31 static int decrypt_key_data(krb5_context context,
32                             int n_key_data, krb5_key_data *key_data,
33                             krb5_keyblock **keyblocks, int *n_keys);
34
35 static krb5_error_code
36 kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
37 {
38     register krb5_principal tempprinc;
39     register int i, nelems;
40
41     tempprinc = (krb5_principal)krb5_db_alloc(context, NULL, sizeof(krb5_principal_data));
42
43     if (tempprinc == 0)
44         return ENOMEM;
45
46     VALGRIND_CHECK_DEFINED(*inprinc);
47     *tempprinc = *inprinc;
48
49     nelems = (int) krb5_princ_size(context, inprinc);
50     tempprinc->data = krb5_db_alloc(context, NULL, nelems * sizeof(krb5_data));
51     if (tempprinc->data == 0) {
52         krb5_db_free(context, (char *)tempprinc);
53         return ENOMEM;
54     }
55
56     for (i = 0; i < nelems; i++) {
57         unsigned int len = krb5_princ_component(context, inprinc, i)->length;
58         krb5_princ_component(context, tempprinc, i)->length = len;
59         if (((krb5_princ_component(context, tempprinc, i)->data =
60               krb5_db_alloc(context, NULL, len)) == 0) && len) {
61             while (--i >= 0)
62                 krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
63             krb5_db_free (context, tempprinc->data);
64             krb5_db_free (context, tempprinc);
65             return ENOMEM;
66         }
67         if (len)
68             memcpy(krb5_princ_component(context, tempprinc, i)->data,
69                    krb5_princ_component(context, inprinc, i)->data, len);
70         krb5_princ_component(context, tempprinc, i)->magic = KV5M_DATA;
71     }
72
73     tempprinc->realm.data =
74         krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length);
75     if (!tempprinc->realm.data && tempprinc->realm.length) {
76         for (i = 0; i < nelems; i++)
77             krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
78         krb5_db_free(context, tempprinc->data);
79         krb5_db_free(context, tempprinc);
80         return ENOMEM;
81     }
82     if (tempprinc->realm.length)
83         memcpy(tempprinc->realm.data, inprinc->realm.data,
84                inprinc->realm.length);
85
86     *outprinc = tempprinc;
87     return 0;
88 }
89
90 static void
91 kadm5_free_principal(krb5_context context, krb5_principal val)
92 {
93     register krb5_int32 i;
94
95     if (!val)
96         return;
97
98     if (val->data) {
99         i = krb5_princ_size(context, val);
100         while(--i >= 0)
101             krb5_db_free(context, krb5_princ_component(context, val, i)->data);
102         krb5_db_free(context, val->data);
103     }
104     if (val->realm.data)
105         krb5_db_free(context, val->realm.data);
106     krb5_db_free(context, val);
107 }
108
109 /*
110  * XXX Functions that ought to be in libkrb5.a, but aren't.
111  */
112 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
113     krb5_context context;
114     krb5_key_data *from, *to;
115 {
116     int i, idx;
117
118     *to = *from;
119
120     idx = (from->key_data_ver == 1 ? 1 : 2);
121
122     for (i = 0; i < idx; i++) {
123         if ( from->key_data_length[i] ) {
124             to->key_data_contents[i] = malloc(from->key_data_length[i]);
125             if (to->key_data_contents[i] == NULL) {
126                 for (i = 0; i < idx; i++) {
127                     if (to->key_data_contents[i]) {
128                         memset(to->key_data_contents[i], 0,
129                                to->key_data_length[i]);
130                         free(to->key_data_contents[i]);
131                     }
132                 }
133                 return ENOMEM;
134             }
135             memcpy(to->key_data_contents[i], from->key_data_contents[i],
136                    from->key_data_length[i]);
137         }
138     }
139     return 0;
140 }
141
142 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
143 {
144     krb5_tl_data *n;
145
146     n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
147     if (n == NULL)
148         return NULL;
149     n->tl_data_contents = malloc(tl->tl_data_length);
150     if (n->tl_data_contents == NULL) {
151         free(n);
152         return NULL;
153     }
154     memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
155     n->tl_data_type = tl->tl_data_type;
156     n->tl_data_length = tl->tl_data_length;
157     n->tl_data_next = NULL;
158     return n;
159 }
160
161 /* This is in lib/kdb/kdb_cpw.c, but is static */
162 static void cleanup_key_data(context, count, data)
163     krb5_context   context;
164     int                    count;
165     krb5_key_data        * data;
166 {
167     int i, j;
168
169     for (i = 0; i < count; i++)
170         for (j = 0; j < data[i].key_data_ver; j++)
171             if (data[i].key_data_length[j])
172                 krb5_db_free(context, data[i].key_data_contents[j]);
173     krb5_db_free(context, data);
174 }
175
176 /*
177  * Set *passptr to NULL if the request looks like the first part of a krb5 1.6
178  * addprinc -randkey operation.  The krb5 1.6 dummy password for these requests
179  * was invalid UTF-8, which runs afoul of the arcfour string-to-key.
180  */
181 static void
182 check_1_6_dummy(kadm5_principal_ent_t entry, long mask,
183                 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char **passptr)
184 {
185     int i;
186     char *password = *passptr;
187
188     /* Old-style randkey operations disallowed tickets to start. */
189     if (!(mask & KADM5_ATTRIBUTES) ||
190         !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX))
191         return;
192
193     /* The 1.6 dummy password was the octets 1..255. */
194     for (i = 0; (unsigned char) password[i] == i + 1; i++);
195     if (password[i] != '\0' || i != 255)
196         return;
197
198     /* This will make the caller use a random password instead. */
199     *passptr = NULL;
200 }
201
202 kadm5_ret_t
203 kadm5_create_principal(void *server_handle,
204                        kadm5_principal_ent_t entry, long mask,
205                        char *password)
206 {
207     return
208         kadm5_create_principal_3(server_handle, entry, mask,
209                                  0, NULL, password);
210 }
211 kadm5_ret_t
212 kadm5_create_principal_3(void *server_handle,
213                          kadm5_principal_ent_t entry, long mask,
214                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
215                          char *password)
216 {
217     krb5_db_entry               *kdb;
218     osa_princ_ent_rec           adb;
219     kadm5_policy_ent_rec        polent;
220     krb5_boolean                have_polent = FALSE;
221     krb5_int32                  now;
222     krb5_tl_data                *tl_data_orig, *tl_data_tail;
223     unsigned int                ret;
224     kadm5_server_handle_t handle = server_handle;
225     krb5_keyblock               *act_mkey;
226     krb5_kvno                   act_kvno;
227
228     CHECK_HANDLE(server_handle);
229
230     krb5_clear_error_message(handle->context);
231
232     check_1_6_dummy(entry, mask, n_ks_tuple, ks_tuple, &password);
233
234     /*
235      * Argument sanity checking, and opening up the DB
236      */
237     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
238        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
239        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
240        (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
241        (mask & KADM5_LAST_FAILED) || (mask & KADM5_FAIL_AUTH_COUNT))
242         return KADM5_BAD_MASK;
243     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
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, active_mkey_list, &act_kvno,
368                                  &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     krb5_error_code ret;
729     kadm5_server_handle_t handle = server_handle;
730     krb5_int16 stype, i;
731     krb5_data *salt = NULL;
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         ret = krb5_dbe_compute_salt(handle->context, &kdb->key_data[i],
751                                     kdb->princ, &stype, &salt);
752         if (ret == KRB5_KDB_BAD_SALTTYPE)
753             ret = KADM5_NO_RENAME_SALT;
754         if (ret)
755             goto done;
756         kdb->key_data[i].key_data_type[1] = KRB5_KDB_SALTTYPE_SPECIAL;
757         free(kdb->key_data[i].key_data_contents[1]);
758         kdb->key_data[i].key_data_contents[1] = (krb5_octet *)salt->data;
759         kdb->key_data[i].key_data_length[1] = salt->length;
760         kdb->key_data[i].key_data_ver = 2;
761         free(salt);
762         salt = NULL;
763     }
764
765     kadm5_free_principal(handle->context, kdb->princ);
766     ret = kadm5_copy_principal(handle->context, target, &kdb->princ);
767     if (ret) {
768         kdb->princ = NULL; /* so freeing the dbe doesn't lose */
769         goto done;
770     }
771
772     if ((ret = kdb_put_entry(handle, kdb, &adb)))
773         goto done;
774
775     ret = kdb_delete_entry(handle, source);
776
777 done:
778     krb5_free_data(handle->context, salt);
779     kdb_free_entry(handle, kdb, &adb);
780     return ret;
781 }
782
783 kadm5_ret_t
784 kadm5_get_principal(void *server_handle, krb5_principal principal,
785                     kadm5_principal_ent_t entry,
786                     long in_mask)
787 {
788     krb5_db_entry               *kdb;
789     osa_princ_ent_rec           adb;
790     krb5_error_code             ret = 0;
791     long                        mask;
792     int i;
793     kadm5_server_handle_t handle = server_handle;
794
795     CHECK_HANDLE(server_handle);
796
797     krb5_clear_error_message(handle->context);
798
799     /*
800      * In version 1, all the defined fields are always returned.
801      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
802      * filled with allocated memory.
803      */
804     mask = in_mask;
805
806     memset(entry, 0, sizeof(*entry));
807
808     if (principal == NULL)
809         return EINVAL;
810
811     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
812         return ret;
813
814     if ((mask & KADM5_POLICY) &&
815         adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
816         if ((entry->policy = strdup(adb.policy)) == NULL) {
817             ret = ENOMEM;
818             goto done;
819         }
820     }
821
822     if (mask & KADM5_AUX_ATTRIBUTES)
823         entry->aux_attributes = adb.aux_attributes;
824
825     if ((mask & KADM5_PRINCIPAL) &&
826         (ret = krb5_copy_principal(handle->context, kdb->princ,
827                                    &entry->principal))) {
828         goto done;
829     }
830
831     if (mask & KADM5_PRINC_EXPIRE_TIME)
832         entry->princ_expire_time = kdb->expiration;
833
834     if ((mask & KADM5_LAST_PWD_CHANGE) &&
835         (ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
836                                                &(entry->last_pwd_change)))) {
837         goto done;
838     }
839
840     if (mask & KADM5_PW_EXPIRATION)
841         entry->pw_expiration = kdb->pw_expiration;
842     if (mask & KADM5_MAX_LIFE)
843         entry->max_life = kdb->max_life;
844
845     /* this is a little non-sensical because the function returns two */
846     /* values that must be checked separately against the mask */
847     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
848         ret = krb5_dbe_lookup_mod_princ_data(handle->context, kdb,
849                                              &(entry->mod_date),
850                                              &(entry->mod_name));
851         if (ret) {
852             goto done;
853         }
854
855         if (! (mask & KADM5_MOD_TIME))
856             entry->mod_date = 0;
857         if (! (mask & KADM5_MOD_NAME)) {
858             krb5_free_principal(handle->context, entry->mod_name);
859             entry->mod_name = NULL;
860         }
861     }
862
863     if (mask & KADM5_ATTRIBUTES)
864         entry->attributes = kdb->attributes;
865
866     if (mask & KADM5_KVNO)
867         for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++)
868             if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno)
869                 entry->kvno = kdb->key_data[i].key_data_kvno;
870
871     if (mask & KADM5_MKVNO) {
872         ret = krb5_dbe_get_mkvno(handle->context, kdb, &entry->mkvno);
873         if (ret)
874             goto done;
875     }
876
877     if (mask & KADM5_MAX_RLIFE)
878         entry->max_renewable_life = kdb->max_renewable_life;
879     if (mask & KADM5_LAST_SUCCESS)
880         entry->last_success = kdb->last_success;
881     if (mask & KADM5_LAST_FAILED)
882         entry->last_failed = kdb->last_failed;
883     if (mask & KADM5_FAIL_AUTH_COUNT)
884         entry->fail_auth_count = kdb->fail_auth_count;
885     if (mask & KADM5_TL_DATA) {
886         krb5_tl_data *tl, *tl2;
887
888         entry->tl_data = NULL;
889
890         tl = kdb->tl_data;
891         while (tl) {
892             if (tl->tl_data_type > 255) {
893                 if ((tl2 = dup_tl_data(tl)) == NULL) {
894                     ret = ENOMEM;
895                     goto done;
896                 }
897                 tl2->tl_data_next = entry->tl_data;
898                 entry->tl_data = tl2;
899                 entry->n_tl_data++;
900             }
901
902             tl = tl->tl_data_next;
903         }
904     }
905     if (mask & KADM5_KEY_DATA) {
906         entry->n_key_data = kdb->n_key_data;
907         if(entry->n_key_data) {
908             entry->key_data = malloc(entry->n_key_data*sizeof(krb5_key_data));
909             if (entry->key_data == NULL) {
910                 ret = ENOMEM;
911                 goto done;
912             }
913         } else
914             entry->key_data = NULL;
915
916         for (i = 0; i < entry->n_key_data; i++)
917             ret = krb5_copy_key_data_contents(handle->context,
918                                               &kdb->key_data[i],
919                                               &entry->key_data[i]);
920         if (ret)
921             goto done;
922     }
923
924     ret = KADM5_OK;
925
926 done:
927     if (ret && entry->principal) {
928         krb5_free_principal(handle->context, entry->principal);
929         entry->principal = NULL;
930     }
931     kdb_free_entry(handle, kdb, &adb);
932
933     return ret;
934 }
935
936 /*
937  * Function: check_pw_reuse
938  *
939  * Purpose: Check if a key appears in a list of keys, in order to
940  * enforce password history.
941  *
942  * Arguments:
943  *
944  *      context                 (r) the krb5 context
945  *      hist_keyblock           (r) the key that hist_key_data is
946  *                              encrypted in
947  *      n_new_key_data          (r) length of new_key_data
948  *      new_key_data            (r) keys to check against
949  *                              pw_hist_data, encrypted in hist_keyblock
950  *      n_pw_hist_data          (r) length of pw_hist_data
951  *      pw_hist_data            (r) passwords to check new_key_data against
952  *
953  * Effects:
954  * For each new_key in new_key_data:
955  *      decrypt new_key with the master_keyblock
956  *      for each password in pw_hist_data:
957  *              for each hist_key in password:
958  *                      decrypt hist_key with hist_keyblock
959  *                      compare the new_key and hist_key
960  *
961  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
962  * new_key_data is the same as a key in pw_hist_data, or 0.
963  */
964 static kadm5_ret_t
965 check_pw_reuse(krb5_context context,
966                krb5_keyblock *hist_keyblocks,
967                int n_new_key_data, krb5_key_data *new_key_data,
968                unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
969 {
970     unsigned int x, y, z;
971     krb5_keyblock newkey, histkey, *kb;
972     krb5_key_data *key_data;
973     krb5_error_code ret;
974
975     assert (n_new_key_data >= 0);
976     for (x = 0; x < (unsigned) n_new_key_data; x++) {
977         /* Check only entries with the most recent kvno. */
978         if (new_key_data[x].key_data_kvno != new_key_data[0].key_data_kvno)
979             break;
980         ret = krb5_dbe_decrypt_key_data(context, NULL, &(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 < (unsigned int) pw_hist_data[y].n_key_data; z++) {
986                 for (kb = hist_keyblocks; kb->enctype != 0; kb++) {
987                     key_data = &pw_hist_data[y].key_data[z];
988                     ret = krb5_dbe_decrypt_key_data(context, kb, key_data,
989                                                     &histkey, NULL);
990                     if (ret)
991                         continue;
992                     if (newkey.length == histkey.length &&
993                         newkey.enctype == histkey.enctype &&
994                         memcmp(newkey.contents, histkey.contents,
995                                histkey.length) == 0) {
996                         krb5_free_keyblock_contents(context, &histkey);
997                         krb5_free_keyblock_contents(context, &newkey);
998                         return KADM5_PASS_REUSE;
999                     }
1000                     krb5_free_keyblock_contents(context, &histkey);
1001                 }
1002             }
1003         }
1004         krb5_free_keyblock_contents(context, &newkey);
1005     }
1006
1007     return(0);
1008 }
1009
1010 /*
1011  * Function: create_history_entry
1012  *
1013  * Purpose: Creates a password history entry from an array of
1014  * key_data.
1015  *
1016  * Arguments:
1017  *
1018  *      context         (r) krb5_context to use
1019  *      mkey            (r) master keyblock to decrypt key data with
1020  *      hist_key        (r) history keyblock to encrypt key data with
1021  *      n_key_data      (r) number of elements in key_data
1022  *      key_data        (r) keys to add to the history entry
1023  *      hist            (w) history entry to fill in
1024  *
1025  * Effects:
1026  *
1027  * hist->key_data is allocated to store n_key_data key_datas.  Each
1028  * element of key_data is decrypted with master_keyblock, re-encrypted
1029  * in hist_key, and added to hist->key_data.  hist->n_key_data is
1030  * set to n_key_data.
1031  */
1032 static
1033 int create_history_entry(krb5_context context,
1034                          krb5_keyblock *hist_key, int n_key_data,
1035                          krb5_key_data *key_data, osa_pw_hist_ent *hist)
1036 {
1037     int i, ret;
1038     krb5_keyblock key;
1039     krb5_keysalt salt;
1040
1041     hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
1042     if (hist->key_data == NULL)
1043         return ENOMEM;
1044     memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
1045
1046     for (i = 0; i < n_key_data; i++) {
1047         ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &key,
1048                                         &salt);
1049         if (ret)
1050             return ret;
1051
1052         ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
1053                                         key_data[i].key_data_kvno,
1054                                         &hist->key_data[i]);
1055         if (ret)
1056             return ret;
1057
1058         krb5_free_keyblock_contents(context, &key);
1059         /* krb5_free_keysalt(context, &salt); */
1060     }
1061
1062     hist->n_key_data = n_key_data;
1063     return 0;
1064 }
1065
1066 static
1067 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
1068 {
1069     int i;
1070
1071     for (i = 0; i < hist->n_key_data; i++)
1072         krb5_free_key_data_contents(context, &hist->key_data[i]);
1073     free(hist->key_data);
1074 }
1075
1076 /*
1077  * Function: add_to_history
1078  *
1079  * Purpose: Adds a password to a principal's password history.
1080  *
1081  * Arguments:
1082  *
1083  *      context         (r) krb5_context to use
1084  *      hist_kvno       (r) kvno of current history key
1085  *      adb             (r/w) admin principal entry to add keys to
1086  *      pol             (r) adb's policy
1087  *      pw              (r) keys for the password to add to adb's key history
1088  *
1089  * Effects:
1090  *
1091  * add_to_history adds a single password to adb's password history.
1092  * pw contains n_key_data keys in its key_data, in storage should be
1093  * allocated but not freed by the caller (XXX blech!).
1094  *
1095  * This function maintains adb->old_keys as a circular queue.  It
1096  * starts empty, and grows each time this function is called until it
1097  * is pol->pw_history_num items long.  adb->old_key_len holds the
1098  * number of allocated entries in the array, and must therefore be [0,
1099  * pol->pw_history_num).  adb->old_key_next is the index into the
1100  * array where the next element should be written, and must be [0,
1101  * adb->old_key_len).
1102  */
1103 static kadm5_ret_t add_to_history(krb5_context context,
1104                                   krb5_kvno hist_kvno,
1105                                   osa_princ_ent_t adb,
1106                                   kadm5_policy_ent_t pol,
1107                                   osa_pw_hist_ent *pw)
1108 {
1109     osa_pw_hist_ent *histp;
1110     uint32_t nhist;
1111     unsigned int i, knext, nkeys;
1112
1113     nhist = pol->pw_history_num;
1114     /* A history of 1 means just check the current password */
1115     if (nhist <= 1)
1116         return 0;
1117
1118     if (adb->admin_history_kvno != hist_kvno) {
1119         /* The history key has changed since the last password change, so we
1120          * have to reset the password history. */
1121         free(adb->old_keys);
1122         adb->old_keys = NULL;
1123         adb->old_key_len = 0;
1124         adb->old_key_next = 0;
1125         adb->admin_history_kvno = hist_kvno;
1126     }
1127
1128     nkeys = adb->old_key_len;
1129     knext = adb->old_key_next;
1130     /* resize the adb->old_keys array if necessary */
1131     if (nkeys + 1 < nhist) {
1132         if (adb->old_keys == NULL) {
1133             adb->old_keys = (osa_pw_hist_ent *)
1134                 malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
1135         } else {
1136             adb->old_keys = (osa_pw_hist_ent *)
1137                 realloc(adb->old_keys,
1138                         (nkeys + 1) * sizeof (osa_pw_hist_ent));
1139         }
1140         if (adb->old_keys == NULL)
1141             return(ENOMEM);
1142
1143         memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
1144         nkeys = ++adb->old_key_len;
1145         /*
1146          * To avoid losing old keys, shift forward each entry after
1147          * knext.
1148          */
1149         for (i = nkeys - 1; i > knext; i--) {
1150             adb->old_keys[i] = adb->old_keys[i - 1];
1151         }
1152         memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
1153     } else if (nkeys + 1 > nhist) {
1154         /*
1155          * The policy must have changed!  Shrink the array.
1156          * Can't simply realloc() down, since it might be wrapped.
1157          * To understand the arithmetic below, note that we are
1158          * copying into new positions 0 .. N-1 from old positions
1159          * old_key_next-N .. old_key_next-1, modulo old_key_len,
1160          * where N = pw_history_num - 1 is the length of the
1161          * shortened list.        Matt Crawford, FNAL
1162          */
1163         /*
1164          * M = adb->old_key_len, N = pol->pw_history_num - 1
1165          *
1166          * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
1167          */
1168         int j;
1169         osa_pw_hist_t tmp;
1170
1171         tmp = (osa_pw_hist_ent *)
1172             malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
1173         if (tmp == NULL)
1174             return ENOMEM;
1175         for (i = 0; i < nhist - 1; i++) {
1176             /*
1177              * Add nkeys once before taking remainder to avoid
1178              * negative values.
1179              */
1180             j = (i + nkeys + knext - (nhist - 1)) % nkeys;
1181             tmp[i] = adb->old_keys[j];
1182         }
1183         /* Now free the ones we don't keep (the oldest ones) */
1184         for (i = 0; i < nkeys - (nhist - 1); i++) {
1185             j = (i + nkeys + knext) % nkeys;
1186             histp = &adb->old_keys[j];
1187             for (j = 0; j < histp->n_key_data; j++) {
1188                 krb5_free_key_data_contents(context, &histp->key_data[j]);
1189             }
1190             free(histp->key_data);
1191         }
1192         free(adb->old_keys);
1193         adb->old_keys = tmp;
1194         nkeys = adb->old_key_len = nhist - 1;
1195         knext = adb->old_key_next = 0;
1196     }
1197
1198     /*
1199      * If nhist decreased since the last password change, and nkeys+1
1200      * is less than the previous nhist, it is possible for knext to
1201      * index into unallocated space.  This condition would not be
1202      * caught by the resizing code above.
1203      */
1204     if (knext + 1 > nkeys)
1205         knext = adb->old_key_next = 0;
1206     /* free the old pw history entry if it contains data */
1207     histp = &adb->old_keys[knext];
1208     for (i = 0; i < (unsigned int) histp->n_key_data; i++)
1209         krb5_free_key_data_contents(context, &histp->key_data[i]);
1210     free(histp->key_data);
1211
1212     /* store the new entry */
1213     adb->old_keys[knext] = *pw;
1214
1215     /* update the next pointer */
1216     if (++adb->old_key_next == nhist - 1)
1217         adb->old_key_next = 0;
1218
1219     return(0);
1220 }
1221
1222 /* FIXME: don't use global variable for this */
1223 krb5_boolean use_password_server = 0;
1224
1225 #ifdef USE_PASSWORD_SERVER
1226 static krb5_boolean
1227 kadm5_use_password_server (void)
1228 {
1229     return use_password_server;
1230 }
1231 #endif
1232
1233 void
1234 kadm5_set_use_password_server (void)
1235 {
1236     use_password_server = 1;
1237 }
1238
1239 #ifdef USE_PASSWORD_SERVER
1240
1241 /*
1242  * kadm5_launch_task () runs a program (task_path) to synchronize the
1243  * Apple password server with the Kerberos database.  Password server
1244  * programs can receive arguments on the command line (task_argv)
1245  * and a block of data via stdin (data_buffer).
1246  *
1247  * Because a failure to communicate with the tool results in the
1248  * password server falling out of sync with the database,
1249  * kadm5_launch_task() always fails if it can't talk to the tool.
1250  */
1251
1252 static kadm5_ret_t
1253 kadm5_launch_task (krb5_context context,
1254                    const char *task_path, char * const task_argv[],
1255                    const char *buffer)
1256 {
1257     kadm5_ret_t ret;
1258     int data_pipe[2];
1259
1260     ret = pipe (data_pipe);
1261     if (ret)
1262         ret = errno;
1263
1264     if (!ret) {
1265         pid_t pid = fork ();
1266         if (pid == -1) {
1267             ret = errno;
1268             close (data_pipe[0]);
1269             close (data_pipe[1]);
1270         } else if (pid == 0) {
1271             /* The child: */
1272
1273             if (dup2 (data_pipe[0], STDIN_FILENO) == -1)
1274                 _exit (1);
1275
1276             close (data_pipe[0]);
1277             close (data_pipe[1]);
1278
1279             execv (task_path, task_argv);
1280
1281             _exit (1); /* Fail if execv fails */
1282         } else {
1283             /* The parent: */
1284             int status;
1285
1286             ret = 0;
1287
1288             close (data_pipe[0]);
1289
1290             /* Write out the buffer to the child, add \n */
1291             if (buffer) {
1292                 if (krb5_net_write (context, data_pipe[1], buffer, strlen (buffer)) < 0
1293                     || krb5_net_write (context, data_pipe[1], "\n", 1) < 0)
1294                 {
1295                     /* kill the child to make sure waitpid() won't hang later */
1296                     ret = errno;
1297                     kill (pid, SIGKILL);
1298                 }
1299             }
1300             close (data_pipe[1]);
1301
1302             waitpid (pid, &status, 0);
1303
1304             if (!ret) {
1305                 if (WIFEXITED (status)) {
1306                     /* child read password and exited.  Check the return value. */
1307                     if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
1308                         ret = KRB5KDC_ERR_POLICY; /* password change rejected */
1309                     }
1310                 } else {
1311                     /* child read password but crashed or was killed */
1312                     ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1313                 }
1314             }
1315         }
1316     }
1317
1318     return ret;
1319 }
1320
1321 #endif
1322
1323 kadm5_ret_t
1324 kadm5_chpass_principal(void *server_handle,
1325                        krb5_principal principal, char *password)
1326 {
1327     return
1328         kadm5_chpass_principal_3(server_handle, principal, FALSE,
1329                                  0, NULL, password);
1330 }
1331
1332 kadm5_ret_t
1333 kadm5_chpass_principal_3(void *server_handle,
1334                          krb5_principal principal, krb5_boolean keepold,
1335                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1336                          char *password)
1337 {
1338     krb5_int32                  now;
1339     kadm5_policy_ent_rec        pol;
1340     osa_princ_ent_rec           adb;
1341     krb5_db_entry               *kdb;
1342     int                         ret, ret2, last_pwd, hist_added;
1343     int                         have_pol = 0;
1344     kadm5_server_handle_t       handle = server_handle;
1345     osa_pw_hist_ent             hist;
1346     krb5_keyblock               *act_mkey, *hist_keyblocks = NULL;
1347     krb5_kvno                   act_kvno, hist_kvno;
1348
1349     CHECK_HANDLE(server_handle);
1350
1351     krb5_clear_error_message(handle->context);
1352
1353     hist_added = 0;
1354     memset(&hist, 0, sizeof(hist));
1355
1356     if (principal == NULL || password == NULL)
1357         return EINVAL;
1358     if ((krb5_principal_compare(handle->context,
1359                                 principal, hist_princ)) == TRUE)
1360         return KADM5_PROTECT_PRINCIPAL;
1361
1362     /* Use default keysalts if caller did not provide any. */
1363     if (n_ks_tuple == 0) {
1364         ks_tuple = handle->params.keysalts;
1365         n_ks_tuple = handle->params.num_keysalts;
1366     }
1367
1368     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1369         return(ret);
1370
1371     if ((adb.aux_attributes & KADM5_POLICY)) {
1372         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1373             goto done;
1374         have_pol = 1;
1375
1376         /* Create a password history entry before we change kdb's key_data. */
1377         ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
1378         if (ret)
1379             goto done;
1380         ret = create_history_entry(handle->context, &hist_keyblocks[0],
1381                                    kdb->n_key_data, kdb->key_data, &hist);
1382         if (ret)
1383             goto done;
1384     }
1385
1386     if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
1387                             principal)))
1388         goto done;
1389
1390     ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno,
1391                                  &act_mkey);
1392     if (ret)
1393         goto done;
1394
1395     ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
1396                        password, 0 /* increment kvno */,
1397                        keepold, kdb);
1398     if (ret)
1399         goto done;
1400
1401     ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
1402     if (ret)
1403         goto done;
1404
1405     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1406
1407     ret = krb5_timeofday(handle->context, &now);
1408     if (ret)
1409         goto done;
1410
1411     if ((adb.aux_attributes & KADM5_POLICY)) {
1412         /* the policy was loaded before */
1413
1414         ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
1415         if (ret)
1416             goto done;
1417
1418 #if 0
1419         /*
1420          * The spec says this check is overridden if the caller has
1421          * modify privilege.  The admin server therefore makes this
1422          * check itself (in chpass_principal_wrapper, misc.c). A
1423          * local caller implicitly has all authorization bits.
1424          */
1425         if ((now - last_pwd) < pol.pw_min_life &&
1426             !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1427             ret = KADM5_PASS_TOOSOON;
1428             goto done;
1429         }
1430 #endif
1431
1432         ret = check_pw_reuse(handle->context, hist_keyblocks,
1433                              kdb->n_key_data, kdb->key_data,
1434                              1, &hist);
1435         if (ret)
1436             goto done;
1437
1438         if (pol.pw_history_num > 1) {
1439             /* If hist_kvno has changed since the last password change, we
1440              * can't check the history. */
1441             if (adb.admin_history_kvno == hist_kvno) {
1442                 ret = check_pw_reuse(handle->context, hist_keyblocks,
1443                                      kdb->n_key_data, kdb->key_data,
1444                                      adb.old_key_len, adb.old_keys);
1445                 if (ret)
1446                     goto done;
1447             }
1448
1449             ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
1450                                  &hist);
1451             if (ret)
1452                 goto done;
1453             hist_added = 1;
1454         }
1455
1456         if (pol.pw_max_life)
1457             kdb->pw_expiration = now + pol.pw_max_life;
1458         else
1459             kdb->pw_expiration = 0;
1460     } else {
1461         kdb->pw_expiration = 0;
1462     }
1463
1464 #ifdef USE_PASSWORD_SERVER
1465     if (kadm5_use_password_server () &&
1466         (krb5_princ_size (handle->context, principal) == 1)) {
1467         krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
1468         const char *path = "/usr/sbin/mkpassdb";
1469         char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
1470         char *pstring = NULL;
1471
1472         if (!ret) {
1473             pstring = malloc ((princ->length + 1) * sizeof (char));
1474             if (pstring == NULL) { ret = ENOMEM; }
1475         }
1476
1477         if (!ret) {
1478             memcpy (pstring, princ->data, princ->length);
1479             pstring [princ->length] = '\0';
1480             argv[2] = pstring;
1481
1482             ret = kadm5_launch_task (handle->context, path, argv, password);
1483         }
1484
1485         if (pstring != NULL)
1486             free (pstring);
1487
1488         if (ret)
1489             goto done;
1490     }
1491 #endif
1492
1493     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1494     if (ret)
1495         goto done;
1496
1497     /* unlock principal on this KDC */
1498     kdb->fail_auth_count = 0;
1499
1500     /* key data and attributes changed, let the database provider know */
1501     kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES |
1502         KADM5_FAIL_AUTH_COUNT;
1503     /* | KADM5_CPW_FUNCTION */
1504
1505     ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1506                                KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1507                                n_ks_tuple, ks_tuple, password);
1508     if (ret)
1509         goto done;
1510
1511     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1512         goto done;
1513
1514     (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1515                                 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1516                                 keepold, n_ks_tuple, ks_tuple, password);
1517     ret = KADM5_OK;
1518 done:
1519     if (!hist_added && hist.key_data)
1520         free_history_entry(handle->context, &hist);
1521     kdb_free_entry(handle, kdb, &adb);
1522     kdb_free_keyblocks(handle, hist_keyblocks);
1523
1524     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1525         && !ret)
1526         ret = ret2;
1527
1528     return ret;
1529 }
1530
1531 kadm5_ret_t
1532 kadm5_randkey_principal(void *server_handle,
1533                         krb5_principal principal,
1534                         krb5_keyblock **keyblocks,
1535                         int *n_keys)
1536 {
1537     return
1538         kadm5_randkey_principal_3(server_handle, principal,
1539                                   FALSE, 0, NULL,
1540                                   keyblocks, n_keys);
1541 }
1542 kadm5_ret_t
1543 kadm5_randkey_principal_3(void *server_handle,
1544                           krb5_principal principal,
1545                           krb5_boolean keepold,
1546                           int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1547                           krb5_keyblock **keyblocks,
1548                           int *n_keys)
1549 {
1550     krb5_db_entry               *kdb;
1551     osa_princ_ent_rec           adb;
1552     krb5_int32                  now;
1553     kadm5_policy_ent_rec        pol;
1554     int                         ret, last_pwd, have_pol = 0;
1555     kadm5_server_handle_t       handle = server_handle;
1556     krb5_keyblock               *act_mkey;
1557
1558     if (keyblocks)
1559         *keyblocks = NULL;
1560
1561     CHECK_HANDLE(server_handle);
1562
1563     /* Use default keysalts if caller did not provide any. */
1564     if (n_ks_tuple == 0) {
1565         ks_tuple = handle->params.keysalts;
1566         n_ks_tuple = handle->params.num_keysalts;
1567     }
1568
1569     krb5_clear_error_message(handle->context);
1570
1571     if (principal == NULL)
1572         return EINVAL;
1573     if (krb5_principal_compare(handle->context, principal, hist_princ)) {
1574         /* If changing the history entry, the new entry must have exactly one
1575          * key. */
1576         if (keepold)
1577             return KADM5_PROTECT_PRINCIPAL;
1578         n_ks_tuple = 1;
1579     }
1580
1581     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1582         return(ret);
1583
1584     ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
1585                                  &act_mkey);
1586     if (ret)
1587         goto done;
1588
1589     ret = krb5_dbe_crk(handle->context, act_mkey, ks_tuple, n_ks_tuple,
1590                        keepold, kdb);
1591     if (ret)
1592         goto done;
1593
1594     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1595
1596     ret = krb5_timeofday(handle->context, &now);
1597     if (ret)
1598         goto done;
1599
1600     if ((adb.aux_attributes & KADM5_POLICY)) {
1601         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1602                                     &pol)) != KADM5_OK)
1603             goto done;
1604         have_pol = 1;
1605
1606         ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
1607         if (ret)
1608             goto done;
1609
1610 #if 0
1611         /*
1612          * The spec says this check is overridden if the caller has
1613          * modify privilege.  The admin server therefore makes this
1614          * check itself (in chpass_principal_wrapper, misc.c).  A
1615          * local caller implicitly has all authorization bits.
1616          */
1617         if((now - last_pwd) < pol.pw_min_life &&
1618            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1619             ret = KADM5_PASS_TOOSOON;
1620             goto done;
1621         }
1622 #endif
1623
1624         if (pol.pw_max_life)
1625             kdb->pw_expiration = now + pol.pw_max_life;
1626         else
1627             kdb->pw_expiration = 0;
1628     } else {
1629         kdb->pw_expiration = 0;
1630     }
1631
1632     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1633     if (ret)
1634         goto done;
1635
1636     /* unlock principal on this KDC */
1637     kdb->fail_auth_count = 0;
1638
1639     if (keyblocks) {
1640         ret = decrypt_key_data(handle->context,
1641                                kdb->n_key_data, kdb->key_data,
1642                                keyblocks, n_keys);
1643         if (ret)
1644             goto done;
1645     }
1646
1647     /* key data changed, let the database provider know */
1648     kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT;
1649     /* | KADM5_RANDKEY_USED */;
1650
1651     ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1652                                KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1653                                n_ks_tuple, ks_tuple, NULL);
1654     if (ret)
1655         goto done;
1656     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1657         goto done;
1658
1659     (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1660                                 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1661                                 keepold, n_ks_tuple, ks_tuple, NULL);
1662     ret = KADM5_OK;
1663 done:
1664     kdb_free_entry(handle, kdb, &adb);
1665     if (have_pol)
1666         kadm5_free_policy_ent(handle->lhandle, &pol);
1667
1668     return ret;
1669 }
1670
1671 /*
1672  * kadm5_setv4key_principal:
1673  *
1674  * Set only ONE key of the principal, removing all others.  This key
1675  * must have the DES_CBC_CRC enctype and is entered as having the
1676  * krb4 salttype.  This is to enable things like kadmind4 to work.
1677  */
1678 kadm5_ret_t
1679 kadm5_setv4key_principal(void *server_handle,
1680                          krb5_principal principal,
1681                          krb5_keyblock *keyblock)
1682 {
1683     krb5_db_entry               *kdb;
1684     osa_princ_ent_rec           adb;
1685     krb5_int32                  now;
1686     kadm5_policy_ent_rec        pol;
1687     krb5_keysalt                keysalt;
1688     int                         i, k, kvno, ret, have_pol = 0;
1689 #if 0
1690     int                         last_pwd;
1691 #endif
1692     kadm5_server_handle_t       handle = server_handle;
1693     krb5_key_data               tmp_key_data;
1694     krb5_keyblock               *act_mkey;
1695
1696     memset( &tmp_key_data, 0, sizeof(tmp_key_data));
1697
1698     CHECK_HANDLE(server_handle);
1699
1700     krb5_clear_error_message(handle->context);
1701
1702     if (principal == NULL || keyblock == NULL)
1703         return EINVAL;
1704     if (hist_princ && /* this will be NULL when initializing the databse */
1705         ((krb5_principal_compare(handle->context,
1706                                  principal, hist_princ)) == TRUE))
1707         return KADM5_PROTECT_PRINCIPAL;
1708
1709     if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1710         return KADM5_SETV4KEY_INVAL_ENCTYPE;
1711
1712     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1713         return(ret);
1714
1715     for (kvno = 0, i=0; i<kdb->n_key_data; i++)
1716         if (kdb->key_data[i].key_data_kvno > kvno)
1717             kvno = kdb->key_data[i].key_data_kvno;
1718
1719     if (kdb->key_data != NULL)
1720         cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1721
1722     kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
1723     if (kdb->key_data == NULL)
1724         return ENOMEM;
1725     memset(kdb->key_data, 0, sizeof(krb5_key_data));
1726     kdb->n_key_data = 1;
1727     keysalt.type = KRB5_KDB_SALTTYPE_V4;
1728     /* XXX data.magic? */
1729     keysalt.data.length = 0;
1730     keysalt.data.data = NULL;
1731
1732     ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
1733                                  &act_mkey);
1734     if (ret)
1735         goto done;
1736
1737     /* use tmp_key_data as temporary location and reallocate later */
1738     ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, keyblock,
1739                                     &keysalt, kvno + 1, &tmp_key_data);
1740     if (ret) {
1741         goto done;
1742     }
1743
1744     for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1745         kdb->key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
1746         kdb->key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
1747         if (tmp_key_data.key_data_contents[k]) {
1748             kdb->key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1749             if (kdb->key_data->key_data_contents[k] == NULL) {
1750                 cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1751                 kdb->key_data = NULL;
1752                 kdb->n_key_data = 0;
1753                 ret = ENOMEM;
1754                 goto done;
1755             }
1756             memcpy (kdb->key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1757
1758             memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1759             free (tmp_key_data.key_data_contents[k]);
1760             tmp_key_data.key_data_contents[k] = NULL;
1761         }
1762     }
1763
1764
1765
1766     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1767
1768     ret = krb5_timeofday(handle->context, &now);
1769     if (ret)
1770         goto done;
1771
1772     if ((adb.aux_attributes & KADM5_POLICY)) {
1773         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1774                                     &pol)) != KADM5_OK)
1775             goto done;
1776         have_pol = 1;
1777
1778 #if 0
1779         /*
1780          * The spec says this check is overridden if the caller has
1781          * modify privilege.  The admin server therefore makes this
1782          * check itself (in chpass_principal_wrapper, misc.c).  A
1783          * local caller implicitly has all authorization bits.
1784          */
1785         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1786                                                   kdb, &last_pwd))
1787             goto done;
1788         if((now - last_pwd) < pol.pw_min_life &&
1789            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1790             ret = KADM5_PASS_TOOSOON;
1791             goto done;
1792         }
1793 #endif
1794
1795         if (pol.pw_max_life)
1796             kdb->pw_expiration = now + pol.pw_max_life;
1797         else
1798             kdb->pw_expiration = 0;
1799     } else {
1800         kdb->pw_expiration = 0;
1801     }
1802
1803     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1804     if (ret)
1805         goto done;
1806
1807     /* unlock principal on this KDC */
1808     kdb->fail_auth_count = 0;
1809
1810     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1811         goto done;
1812
1813     ret = KADM5_OK;
1814 done:
1815     for (i = 0; i < tmp_key_data.key_data_ver; i++) {
1816         if (tmp_key_data.key_data_contents[i]) {
1817             memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
1818             free (tmp_key_data.key_data_contents[i]);
1819         }
1820     }
1821
1822     kdb_free_entry(handle, kdb, &adb);
1823     if (have_pol)
1824         kadm5_free_policy_ent(handle->lhandle, &pol);
1825
1826     return ret;
1827 }
1828
1829 kadm5_ret_t
1830 kadm5_setkey_principal(void *server_handle,
1831                        krb5_principal principal,
1832                        krb5_keyblock *keyblocks,
1833                        int n_keys)
1834 {
1835     return
1836         kadm5_setkey_principal_3(server_handle, principal,
1837                                  FALSE, 0, NULL,
1838                                  keyblocks, n_keys);
1839 }
1840
1841 kadm5_ret_t
1842 kadm5_setkey_principal_3(void *server_handle,
1843                          krb5_principal principal,
1844                          krb5_boolean keepold,
1845                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1846                          krb5_keyblock *keyblocks,
1847                          int n_keys)
1848 {
1849     krb5_db_entry               *kdb;
1850     osa_princ_ent_rec           adb;
1851     krb5_int32                  now;
1852     kadm5_policy_ent_rec        pol;
1853     krb5_key_data               *old_key_data;
1854     int                         n_old_keys;
1855     int                         i, j, k, kvno, ret, have_pol = 0;
1856 #if 0
1857     int                         last_pwd;
1858 #endif
1859     kadm5_server_handle_t       handle = server_handle;
1860     krb5_boolean                similar;
1861     krb5_keysalt                keysalt;
1862     krb5_key_data         tmp_key_data;
1863     krb5_key_data        *tptr;
1864     krb5_keyblock               *act_mkey;
1865
1866     CHECK_HANDLE(server_handle);
1867
1868     krb5_clear_error_message(handle->context);
1869
1870     if (principal == NULL || keyblocks == NULL)
1871         return EINVAL;
1872     if (hist_princ && /* this will be NULL when initializing the databse */
1873         ((krb5_principal_compare(handle->context,
1874                                  principal, hist_princ)) == TRUE))
1875         return KADM5_PROTECT_PRINCIPAL;
1876
1877     for (i = 0; i < n_keys; i++) {
1878         for (j = i+1; j < n_keys; j++) {
1879             if ((ret = krb5_c_enctype_compare(handle->context,
1880                                               keyblocks[i].enctype,
1881                                               keyblocks[j].enctype,
1882                                               &similar)))
1883                 return(ret);
1884             if (similar) {
1885                 if (n_ks_tuple) {
1886                     if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1887                         return KADM5_SETKEY_DUP_ENCTYPES;
1888                 } else
1889                     return KADM5_SETKEY_DUP_ENCTYPES;
1890             }
1891         }
1892     }
1893
1894     if (n_ks_tuple && n_ks_tuple != n_keys)
1895         return KADM5_SETKEY3_ETYPE_MISMATCH;
1896
1897     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1898         return(ret);
1899
1900     for (kvno = 0, i=0; i<kdb->n_key_data; i++)
1901         if (kdb->key_data[i].key_data_kvno > kvno)
1902             kvno = kdb->key_data[i].key_data_kvno;
1903
1904     if (keepold) {
1905         old_key_data = kdb->key_data;
1906         n_old_keys = kdb->n_key_data;
1907     } else {
1908         if (kdb->key_data != NULL)
1909             cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1910         n_old_keys = 0;
1911         old_key_data = NULL;
1912     }
1913
1914     kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
1915                                                   *sizeof(krb5_key_data));
1916     if (kdb->key_data == NULL) {
1917         ret = ENOMEM;
1918         goto done;
1919     }
1920
1921     memset(kdb->key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1922     kdb->n_key_data = 0;
1923
1924     for (i = 0; i < n_keys; i++) {
1925         if (n_ks_tuple) {
1926             keysalt.type = ks_tuple[i].ks_salttype;
1927             keysalt.data.length = 0;
1928             keysalt.data.data = NULL;
1929             if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1930                 ret = KADM5_SETKEY3_ETYPE_MISMATCH;
1931                 goto done;
1932             }
1933         }
1934         memset (&tmp_key_data, 0, sizeof(tmp_key_data));
1935
1936         ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
1937                                      &act_mkey);
1938         if (ret)
1939             goto done;
1940
1941         ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
1942                                         &keyblocks[i],
1943                                         n_ks_tuple ? &keysalt : NULL, kvno + 1,
1944                                         &tmp_key_data);
1945         if (ret)
1946             goto done;
1947
1948         tptr = &kdb->key_data[i];
1949         tptr->key_data_ver = tmp_key_data.key_data_ver;
1950         tptr->key_data_kvno = tmp_key_data.key_data_kvno;
1951         for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1952             tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
1953             tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
1954             if (tmp_key_data.key_data_contents[k]) {
1955                 tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1956                 if (tptr->key_data_contents[k] == NULL) {
1957                     int i1;
1958                     for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
1959                         if (tmp_key_data.key_data_contents[i1]) {
1960                             memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
1961                             free (tmp_key_data.key_data_contents[i1]);
1962                         }
1963                     }
1964
1965                     ret =  ENOMEM;
1966                     goto done;
1967                 }
1968                 memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1969
1970                 memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1971                 free (tmp_key_data.key_data_contents[k]);
1972                 tmp_key_data.key_data_contents[k] = NULL;
1973             }
1974         }
1975         kdb->n_key_data++;
1976     }
1977
1978     /* copy old key data if necessary */
1979     for (i = 0; i < n_old_keys; i++) {
1980         kdb->key_data[i+n_keys] = old_key_data[i];
1981         memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1982         kdb->n_key_data++;
1983     }
1984
1985     if (old_key_data)
1986         krb5_db_free(handle->context, old_key_data);
1987
1988     /* assert(kdb->n_key_data == n_keys + n_old_keys) */
1989     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1990
1991     if ((ret = krb5_timeofday(handle->context, &now)))
1992         goto done;
1993
1994     if ((adb.aux_attributes & KADM5_POLICY)) {
1995         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1996                                     &pol)) != KADM5_OK)
1997             goto done;
1998         have_pol = 1;
1999
2000 #if 0
2001         /*
2002          * The spec says this check is overridden if the caller has
2003          * modify privilege.  The admin server therefore makes this
2004          * check itself (in chpass_principal_wrapper, misc.c).  A
2005          * local caller implicitly has all authorization bits.
2006          */
2007         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
2008                                                   kdb, &last_pwd))
2009             goto done;
2010         if((now - last_pwd) < pol.pw_min_life &&
2011            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2012             ret = KADM5_PASS_TOOSOON;
2013             goto done;
2014         }
2015 #endif
2016
2017         if (pol.pw_max_life)
2018             kdb->pw_expiration = now + pol.pw_max_life;
2019         else
2020             kdb->pw_expiration = 0;
2021     } else {
2022         kdb->pw_expiration = 0;
2023     }
2024
2025     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
2026         goto done;
2027
2028     /* unlock principal on this KDC */
2029     kdb->fail_auth_count = 0;
2030
2031     if ((ret = kdb_put_entry(handle, kdb, &adb)))
2032         goto done;
2033
2034     ret = KADM5_OK;
2035 done:
2036     kdb_free_entry(handle, kdb, &adb);
2037     if (have_pol)
2038         kadm5_free_policy_ent(handle->lhandle, &pol);
2039
2040     return ret;
2041 }
2042
2043 /*
2044  * Return the list of keys like kadm5_randkey_principal,
2045  * but don't modify the principal.
2046  */
2047 kadm5_ret_t
2048 kadm5_get_principal_keys(void *server_handle /* IN */,
2049                          krb5_principal principal /* IN */,
2050                          krb5_keyblock **keyblocks /* OUT */,
2051                          int *n_keys /* OUT */)
2052 {
2053     krb5_db_entry               *kdb;
2054     osa_princ_ent_rec           adb;
2055     kadm5_ret_t                 ret;
2056     kadm5_server_handle_t       handle = server_handle;
2057
2058     if (keyblocks)
2059         *keyblocks = NULL;
2060
2061     CHECK_HANDLE(server_handle);
2062
2063     if (principal == NULL)
2064         return EINVAL;
2065
2066     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
2067         return(ret);
2068
2069     if (keyblocks) {
2070         ret = decrypt_key_data(handle->context,
2071                                kdb->n_key_data, kdb->key_data,
2072                                keyblocks, n_keys);
2073         if (ret)
2074             goto done;
2075     }
2076
2077     ret = KADM5_OK;
2078 done:
2079     kdb_free_entry(handle, kdb, &adb);
2080
2081     return ret;
2082 }
2083
2084
2085 /*
2086  * Allocate an array of n_key_data krb5_keyblocks, fill in each
2087  * element with the results of decrypting the nth key in key_data,
2088  * and if n_keys is not NULL fill it in with the
2089  * number of keys decrypted.
2090  */
2091 static int decrypt_key_data(krb5_context context,
2092                             int n_key_data, krb5_key_data *key_data,
2093                             krb5_keyblock **keyblocks, int *n_keys)
2094 {
2095     krb5_keyblock *keys;
2096     int ret, i;
2097
2098     keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
2099     if (keys == NULL)
2100         return ENOMEM;
2101     memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
2102
2103     for (i = 0; i < n_key_data; i++) {
2104         ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &keys[i],
2105                                         NULL);
2106         if (ret) {
2107             for (; i >= 0; i--) {
2108                 if (keys[i].contents) {
2109                     memset (keys[i].contents, 0, keys[i].length);
2110                     free( keys[i].contents );
2111                 }
2112             }
2113
2114             memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
2115             free(keys);
2116             return ret;
2117         }
2118     }
2119
2120     *keyblocks = keys;
2121     if (n_keys)
2122         *n_keys = n_key_data;
2123
2124     return 0;
2125 }
2126
2127 /*
2128  * Function: kadm5_decrypt_key
2129  *
2130  * Purpose: Retrieves and decrypts a principal key.
2131  *
2132  * Arguments:
2133  *
2134  *      server_handle   (r) kadm5 handle
2135  *      entry           (r) principal retrieved with kadm5_get_principal
2136  *      ktype           (r) enctype to search for, or -1 to ignore
2137  *      stype           (r) salt type to search for, or -1 to ignore
2138  *      kvno            (r) kvno to search for, -1 for max, 0 for max
2139  *                      only if it also matches ktype and stype
2140  *      keyblock        (w) keyblock to fill in
2141  *      keysalt         (w) keysalt to fill in, or NULL
2142  *      kvnop           (w) kvno to fill in, or NULL
2143  *
2144  * Effects: Searches the key_data array of entry, which must have been
2145  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2146  * find a key with a specified enctype, salt type, and kvno in a
2147  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
2148  * it with the master key, and return the key in keyblock, the salt
2149  * in salttype, and the key version number in kvno.
2150  *
2151  * If ktype or stype is -1, it is ignored for the search.  If kvno is
2152  * -1, ktype and stype are ignored and the key with the max kvno is
2153  * returned.  If kvno is 0, only the key with the max kvno is returned
2154  * and only if it matches the ktype and stype; otherwise, ENOENT is
2155  * returned.
2156  */
2157 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
2158                               kadm5_principal_ent_t entry, krb5_int32
2159                               ktype, krb5_int32 stype, krb5_int32
2160                               kvno, krb5_keyblock *keyblock,
2161                               krb5_keysalt *keysalt, int *kvnop)
2162 {
2163     kadm5_server_handle_t handle = server_handle;
2164     krb5_db_entry dbent;
2165     krb5_key_data *key_data;
2166     krb5_keyblock *mkey_ptr;
2167     int ret;
2168
2169     CHECK_HANDLE(server_handle);
2170
2171     if (entry->n_key_data == 0 || entry->key_data == NULL)
2172         return EINVAL;
2173
2174     /* find_enctype only uses these two fields */
2175     dbent.n_key_data = entry->n_key_data;
2176     dbent.key_data = entry->key_data;
2177     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
2178                                      stype, kvno, &key_data)))
2179         return ret;
2180
2181     /* find_mkey only uses this field */
2182     dbent.tl_data = entry->tl_data;
2183     if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, &mkey_ptr))) {
2184         /* try refreshing master key list */
2185         /* XXX it would nice if we had the mkvno here for optimization */
2186         if (krb5_db_fetch_mkey_list(handle->context, master_princ,
2187                                     &master_keyblock) == 0) {
2188             if ((ret = krb5_dbe_find_mkey(handle->context, &dbent,
2189                                           &mkey_ptr))) {
2190                 return ret;
2191             }
2192         } else {
2193             return ret;
2194         }
2195     }
2196
2197     if ((ret = krb5_dbe_decrypt_key_data(handle->context, NULL, key_data,
2198                                          keyblock, keysalt)))
2199         return ret;
2200
2201     /*
2202      * Coerce the enctype of the output keyblock in case we got an
2203      * inexact match on the enctype; this behavior will go away when
2204      * the key storage architecture gets redesigned for 1.3.
2205      */
2206     if (ktype != -1)
2207         keyblock->enctype = ktype;
2208
2209     if (kvnop)
2210         *kvnop = key_data->key_data_kvno;
2211
2212     return KADM5_OK;
2213 }
2214
2215 kadm5_ret_t
2216 kadm5_purgekeys(void *server_handle,
2217                 krb5_principal principal,
2218                 int keepkvno)
2219 {
2220     kadm5_server_handle_t handle = server_handle;
2221     kadm5_ret_t ret;
2222     krb5_db_entry *kdb;
2223     osa_princ_ent_rec adb;
2224     krb5_key_data *old_keydata;
2225     int n_old_keydata;
2226     int i, j, k;
2227
2228     CHECK_HANDLE(server_handle);
2229
2230     if (principal == NULL)
2231         return EINVAL;
2232
2233     ret = kdb_get_entry(handle, principal, &kdb, &adb);
2234     if (ret)
2235         return(ret);
2236
2237     if (keepkvno <= 0) {
2238         keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
2239                                              kdb->key_data);
2240     }
2241
2242     old_keydata = kdb->key_data;
2243     n_old_keydata = kdb->n_key_data;
2244     kdb->n_key_data = 0;
2245     kdb->key_data = krb5_db_alloc(handle->context, NULL,
2246                                   n_old_keydata * sizeof(krb5_key_data));
2247     if (kdb->key_data == NULL) {
2248         ret = ENOMEM;
2249         goto done;
2250     }
2251     memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
2252     for (i = 0, j = 0; i < n_old_keydata; i++) {
2253         if (old_keydata[i].key_data_kvno < keepkvno)
2254             continue;
2255
2256         /* Alias the key_data_contents pointers; we null them out in the
2257          * source array immediately after. */
2258         kdb->key_data[j] = old_keydata[i];
2259         for (k = 0; k < old_keydata[i].key_data_ver; k++) {
2260             old_keydata[i].key_data_contents[k] = NULL;
2261         }
2262         j++;
2263     }
2264     kdb->n_key_data = j;
2265     cleanup_key_data(handle->context, n_old_keydata, old_keydata);
2266
2267     kdb->mask = KADM5_KEY_DATA;
2268     ret = kdb_put_entry(handle, kdb, &adb);
2269     if (ret)
2270         goto done;
2271
2272 done:
2273     kdb_free_entry(handle, kdb, &adb);
2274     return ret;
2275 }
2276
2277 kadm5_ret_t
2278 kadm5_get_strings(void *server_handle, krb5_principal principal,
2279                   krb5_string_attr **strings_out, int *count_out)
2280 {
2281     kadm5_server_handle_t handle = server_handle;
2282     kadm5_ret_t ret;
2283     krb5_db_entry *kdb = NULL;
2284
2285     *strings_out = NULL;
2286     *count_out = 0;
2287     CHECK_HANDLE(server_handle);
2288     if (principal == NULL)
2289         return EINVAL;
2290
2291     ret = kdb_get_entry(handle, principal, &kdb, NULL);
2292     if (ret)
2293         return ret;
2294
2295     ret = krb5_dbe_get_strings(handle->context, kdb, strings_out, count_out);
2296     kdb_free_entry(handle, kdb, NULL);
2297     return ret;
2298 }
2299
2300 kadm5_ret_t
2301 kadm5_set_string(void *server_handle, krb5_principal principal,
2302                  const char *key, const char *value)
2303 {
2304     kadm5_server_handle_t handle = server_handle;
2305     kadm5_ret_t ret;
2306     krb5_db_entry *kdb;
2307     osa_princ_ent_rec adb;
2308
2309     CHECK_HANDLE(server_handle);
2310     if (principal == NULL || key == NULL)
2311         return EINVAL;
2312
2313     ret = kdb_get_entry(handle, principal, &kdb, &adb);
2314     if (ret)
2315         return ret;
2316
2317     ret = krb5_dbe_set_string(handle->context, kdb, key, value);
2318     if (ret)
2319         goto done;
2320
2321     kdb->mask = KADM5_TL_DATA;
2322     ret = kdb_put_entry(handle, kdb, &adb);
2323
2324 done:
2325     kdb_free_entry(handle, kdb, &adb);
2326     return ret;
2327 }