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