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