2 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
7 #if !defined(lint) && !defined(__CODECENTER__)
8 static char *rcsid = "$Header$";
11 #include <sys/types.h>
13 #include <kadm5/admin.h>
19 #include "server_internal.h"
23 extern krb5_principal master_princ;
24 extern krb5_principal hist_princ;
25 extern krb5_keyblock master_keyblock;
26 extern krb5_keyblock hist_key;
27 extern krb5_db_entry master_db;
28 extern krb5_db_entry hist_db;
29 extern krb5_kvno hist_kvno;
31 static int decrypt_key_data(krb5_context context,
32 int n_key_data, krb5_key_data *key_data,
33 krb5_keyblock **keyblocks, int *n_keys);
36 * XXX Functions that ought to be in libkrb5.a, but aren't.
38 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
40 krb5_key_data *from, *to;
46 idx = (from->key_data_ver == 1 ? 1 : 2);
48 for (i = 0; i < idx; i++) {
49 if ( from->key_data_length[i] ) {
50 to->key_data_contents[i] = malloc(from->key_data_length[i]);
51 if (to->key_data_contents[i] == NULL) {
52 for (i = 0; i < idx; i++) {
53 if (to->key_data_contents[i]) {
54 memset(to->key_data_contents[i], 0,
55 to->key_data_length[i]);
56 free(to->key_data_contents[i]);
61 memcpy(to->key_data_contents[i], from->key_data_contents[i],
62 from->key_data_length[i]);
68 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
72 n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
75 n->tl_data_contents = malloc(tl->tl_data_length);
76 if (n->tl_data_contents == NULL) {
80 memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
81 n->tl_data_type = tl->tl_data_type;
82 n->tl_data_length = tl->tl_data_length;
83 n->tl_data_next = NULL;
87 /* This is in lib/kdb/kdb_cpw.c, but is static */
88 static void cleanup_key_data(context, count, data)
95 for (i = 0; i < count; i++)
96 for (j = 0; j < data[i].key_data_ver; j++)
97 if (data[i].key_data_length[j])
98 free(data[i].key_data_contents[j]);
103 kadm5_create_principal(void *server_handle,
104 kadm5_principal_ent_t entry, long mask,
108 kadm5_create_principal_3(server_handle, entry, mask,
112 kadm5_create_principal_3(void *server_handle,
113 kadm5_principal_ent_t entry, long mask,
114 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
118 osa_princ_ent_rec adb;
119 kadm5_policy_ent_rec polent;
121 krb5_tl_data *tl_data_orig, *tl_data_tail;
123 kadm5_server_handle_t handle = server_handle;
125 CHECK_HANDLE(server_handle);
128 * Argument sanity checking, and opening up the DB
130 if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
131 (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
132 (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
133 (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
134 (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
135 (mask & KADM5_FAIL_AUTH_COUNT))
136 return KADM5_BAD_MASK;
137 if((mask & ~ALL_PRINC_MASK))
138 return KADM5_BAD_MASK;
139 if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
143 * Check to see if the principal exists
145 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
148 case KADM5_UNK_PRINC:
151 kdb_free_entry(handle, &kdb, &adb);
157 memset(&kdb, 0, sizeof(krb5_db_entry));
158 memset(&adb, 0, sizeof(osa_princ_ent_rec));
161 * If a policy was specified, load it.
162 * If we can not find the one specified return an error
164 if ((mask & KADM5_POLICY)) {
165 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
166 &polent)) != KADM5_OK) {
168 return KADM5_BAD_POLICY;
173 if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY),
174 &polent, entry->principal))) {
175 if (mask & KADM5_POLICY)
176 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
180 * Start populating the various DB fields, using the
181 * "defaults" for fields that were not specified by the
184 if ((ret = krb5_timeofday(handle->context, &now))) {
185 if (mask & KADM5_POLICY)
186 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
190 kdb.magic = KRB5_KDB_MAGIC_NUMBER;
191 kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
193 if ((mask & KADM5_ATTRIBUTES))
194 kdb.attributes = entry->attributes;
196 kdb.attributes = handle->params.flags;
198 if ((mask & KADM5_MAX_LIFE))
199 kdb.max_life = entry->max_life;
201 kdb.max_life = handle->params.max_life;
203 if (mask & KADM5_MAX_RLIFE)
204 kdb.max_renewable_life = entry->max_renewable_life;
206 kdb.max_renewable_life = handle->params.max_rlife;
208 if ((mask & KADM5_PRINC_EXPIRE_TIME))
209 kdb.expiration = entry->princ_expire_time;
211 kdb.expiration = handle->params.expiration;
213 kdb.pw_expiration = 0;
214 if ((mask & KADM5_POLICY)) {
215 if(polent.pw_max_life)
216 kdb.pw_expiration = now + polent.pw_max_life;
218 kdb.pw_expiration = 0;
220 if ((mask & KADM5_PW_EXPIRATION))
221 kdb.pw_expiration = entry->pw_expiration;
223 kdb.last_success = 0;
225 kdb.fail_auth_count = 0;
227 /* this is kind of gross, but in order to free the tl data, I need
228 to free the entire kdb entry, and that will try to free the
231 if ((ret = krb5_copy_principal(handle->context,
232 entry->principal, &(kdb.princ)))) {
233 if (mask & KADM5_POLICY)
234 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
238 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) {
239 krb5_dbe_free_contents(handle->context, &kdb);
240 if (mask & KADM5_POLICY)
241 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
245 /* initialize the keys */
247 if ((ret = krb5_dbe_cpw(handle->context, &master_keyblock,
248 n_ks_tuple?ks_tuple:handle->params.keysalts,
249 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
251 (mask & KADM5_KVNO)?entry->kvno:1,
253 krb5_dbe_free_contents(handle->context, &kdb);
254 if (mask & KADM5_POLICY)
255 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
259 /* populate the admin-server-specific fields. In the OV server,
260 this used to be in a separate database. Since there's already
261 marshalling code for the admin fields, to keep things simple,
262 I'm going to keep it, and make all the admin stuff occupy a
263 single tl_data record, */
265 adb.admin_history_kvno = hist_kvno;
266 if ((mask & KADM5_POLICY)) {
267 adb.aux_attributes = KADM5_POLICY;
269 /* this does *not* need to be strdup'ed, because adb is xdr */
270 /* encoded in osa_adb_create_princ, and not ever freed */
272 adb.policy = entry->policy;
275 /* increment the policy ref count, if any */
277 if ((mask & KADM5_POLICY)) {
278 polent.policy_refcnt++;
279 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
282 krb5_dbe_free_contents(handle->context, &kdb);
283 if (mask & KADM5_POLICY)
284 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
289 if (mask & KADM5_TL_DATA) {
290 /* splice entry->tl_data onto the front of kdb.tl_data */
291 tl_data_orig = kdb.tl_data;
292 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
293 tl_data_tail = tl_data_tail->tl_data_next)
295 tl_data_tail->tl_data_next = kdb.tl_data;
296 kdb.tl_data = entry->tl_data;
299 /* store the new db entry */
300 ret = kdb_put_entry(handle, &kdb, &adb);
302 if (mask & KADM5_TL_DATA) {
303 /* remove entry->tl_data from the front of kdb.tl_data */
304 tl_data_tail->tl_data_next = NULL;
305 kdb.tl_data = tl_data_orig;
308 krb5_dbe_free_contents(handle->context, &kdb);
311 if ((mask & KADM5_POLICY)) {
312 /* decrement the policy ref count */
314 polent.policy_refcnt--;
316 * if this fails, there's nothing we can do anyway. the
317 * policy refcount wil be too high.
319 (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
323 if (mask & KADM5_POLICY)
324 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
328 if (mask & KADM5_POLICY)
329 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
336 kadm5_delete_principal(void *server_handle, krb5_principal principal)
339 kadm5_policy_ent_rec polent;
341 osa_princ_ent_rec adb;
342 kadm5_server_handle_t handle = server_handle;
344 CHECK_HANDLE(server_handle);
346 if (principal == NULL)
349 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
352 if ((adb.aux_attributes & KADM5_POLICY)) {
353 if ((ret = kadm5_get_policy(handle->lhandle,
354 adb.policy, &polent))
356 polent.policy_refcnt--;
357 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
360 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
361 kdb_free_entry(handle, &kdb, &adb);
365 if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
366 kdb_free_entry(handle, &kdb, &adb);
371 ret = kdb_delete_entry(handle, principal);
373 kdb_free_entry(handle, &kdb, &adb);
379 kadm5_modify_principal(void *server_handle,
380 kadm5_principal_ent_t entry, long mask)
383 kadm5_policy_ent_rec npol, opol;
384 int have_npol = 0, have_opol = 0;
386 krb5_tl_data *tl_data_orig;
387 osa_princ_ent_rec adb;
388 kadm5_server_handle_t handle = server_handle;
390 CHECK_HANDLE(server_handle);
392 if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
393 (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
394 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
395 (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
396 (mask & KADM5_LAST_FAILED))
397 return KADM5_BAD_MASK;
398 if((mask & ~ALL_PRINC_MASK))
399 return KADM5_BAD_MASK;
400 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
401 return KADM5_BAD_MASK;
402 if(entry == (kadm5_principal_ent_t) NULL)
404 if (mask & KADM5_TL_DATA) {
405 tl_data_orig = entry->tl_data;
406 while (tl_data_orig) {
407 if (tl_data_orig->tl_data_type < 256)
408 return KADM5_BAD_TL_TYPE;
409 tl_data_orig = tl_data_orig->tl_data_next;
413 if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb))
417 * This is pretty much the same as create ...
420 if ((mask & KADM5_POLICY)) {
421 /* get the new policy */
422 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
426 ret = KADM5_BAD_POLICY;
428 case KADM5_UNK_POLICY:
429 case KADM5_BAD_POLICY:
430 ret = KADM5_UNK_POLICY;
437 /* if we already have a policy, get it to decrement the refcnt */
438 if(adb.aux_attributes & KADM5_POLICY) {
439 /* ... but not if the old and new are the same */
440 if(strcmp(adb.policy, entry->policy)) {
441 ret = kadm5_get_policy(handle->lhandle,
445 case KADM5_BAD_POLICY:
446 case KADM5_UNK_POLICY:
450 opol.policy_refcnt--;
456 npol.policy_refcnt++;
458 } else npol.policy_refcnt++;
460 /* set us up to use the new policy */
461 adb.aux_attributes |= KADM5_POLICY;
464 adb.policy = strdup(entry->policy);
466 /* set pw_max_life based on new policy */
467 if (npol.pw_max_life) {
468 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
469 &(kdb.pw_expiration)))
471 kdb.pw_expiration += npol.pw_max_life;
473 kdb.pw_expiration = 0;
477 if ((mask & KADM5_POLICY_CLR) &&
478 (adb.aux_attributes & KADM5_POLICY)) {
479 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
482 case KADM5_BAD_POLICY:
483 case KADM5_UNK_POLICY:
492 adb.aux_attributes &= ~KADM5_POLICY;
493 kdb.pw_expiration = 0;
494 opol.policy_refcnt--;
502 if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
505 kadm5_modify_policy_internal(handle->lhandle, &opol,
506 KADM5_REF_COUNT))) ||
509 kadm5_modify_policy_internal(handle->lhandle, &npol,
513 if ((mask & KADM5_ATTRIBUTES))
514 kdb.attributes = entry->attributes;
515 if ((mask & KADM5_MAX_LIFE))
516 kdb.max_life = entry->max_life;
517 if ((mask & KADM5_PRINC_EXPIRE_TIME))
518 kdb.expiration = entry->princ_expire_time;
519 if (mask & KADM5_PW_EXPIRATION)
520 kdb.pw_expiration = entry->pw_expiration;
521 if (mask & KADM5_MAX_RLIFE)
522 kdb.max_renewable_life = entry->max_renewable_life;
523 if (mask & KADM5_FAIL_AUTH_COUNT)
524 kdb.fail_auth_count = entry->fail_auth_count;
526 if((mask & KADM5_KVNO)) {
527 for (i = 0; i < kdb.n_key_data; i++)
528 kdb.key_data[i].key_data_kvno = entry->kvno;
531 if (mask & KADM5_TL_DATA) {
532 krb5_tl_data *tl, *tl2;
534 * Replace kdb.tl_data with what was passed in. The
535 * KRB5_TL_KADM_DATA will be re-added (based on adb) by
536 * kdb_put_entry, below.
538 * Note that we have to duplicate the passed in tl_data
539 * before adding it to kdb. The reason is that kdb_put_entry
540 * will add its own tl_data entries that we will need to
541 * free, but we cannot free the caller's tl_data (an
542 * alternative would be to scan the tl_data after put_entry
543 * and only free those entries that were not passed in).
545 while (kdb.tl_data) {
546 tl = kdb.tl_data->tl_data_next;
547 free(kdb.tl_data->tl_data_contents);
552 kdb.n_tl_data = entry->n_tl_data;
554 tl2 = entry->tl_data;
556 tl = dup_tl_data(tl2);
557 tl->tl_data_next = kdb.tl_data;
559 tl2 = tl2->tl_data_next;
563 ret = kdb_put_entry(handle, &kdb, &adb);
569 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
570 ret = ret ? ret : ret2;
573 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
574 ret = ret ? ret : ret2;
576 kdb_free_entry(handle, &kdb, &adb);
581 kadm5_rename_principal(void *server_handle,
582 krb5_principal source, krb5_principal target)
585 osa_princ_ent_rec adb;
587 kadm5_server_handle_t handle = server_handle;
589 CHECK_HANDLE(server_handle);
591 if (source == NULL || target == NULL)
594 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
595 kdb_free_entry(handle, &kdb, &adb);
599 if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
602 /* this is kinda gross, but unavoidable */
604 for (i=0; i<kdb.n_key_data; i++) {
605 if ((kdb.key_data[i].key_data_ver == 1) ||
606 (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
607 ret = KADM5_NO_RENAME_SALT;
612 krb5_free_principal(handle->context, kdb.princ);
613 if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) {
614 kdb.princ = NULL; /* so freeing the dbe doesn't lose */
618 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
621 ret = kdb_delete_entry(handle, source);
624 kdb_free_entry(handle, &kdb, &adb);
629 kadm5_get_principal(void *server_handle, krb5_principal principal,
630 kadm5_principal_ent_t entry,
634 osa_princ_ent_rec adb;
635 osa_adb_ret_t ret = 0;
638 kadm5_server_handle_t handle = server_handle;
639 kadm5_principal_ent_rec entry_local, *entry_orig;
641 CHECK_HANDLE(server_handle);
644 * In version 1, all the defined fields are always returned.
645 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
646 * filled with allocated memory.
648 if (handle->api_version == KADM5_API_VERSION_1) {
649 mask = KADM5_PRINCIPAL_NORMAL_MASK;
651 entry = &entry_local;
656 memset((char *) entry, 0, sizeof(*entry));
658 if (principal == NULL)
661 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
664 if ((mask & KADM5_POLICY) &&
665 adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
666 if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
670 strcpy(entry->policy, adb.policy);
673 if (mask & KADM5_AUX_ATTRIBUTES)
674 entry->aux_attributes = adb.aux_attributes;
676 if ((mask & KADM5_PRINCIPAL) &&
677 (ret = krb5_copy_principal(handle->context, principal,
678 &entry->principal))) {
682 if (mask & KADM5_PRINC_EXPIRE_TIME)
683 entry->princ_expire_time = kdb.expiration;
685 if ((mask & KADM5_LAST_PWD_CHANGE) &&
686 (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
687 &(entry->last_pwd_change)))) {
691 if (mask & KADM5_PW_EXPIRATION)
692 entry->pw_expiration = kdb.pw_expiration;
693 if (mask & KADM5_MAX_LIFE)
694 entry->max_life = kdb.max_life;
696 /* this is a little non-sensical because the function returns two */
697 /* values that must be checked separately against the mask */
698 if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
699 if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
701 &(entry->mod_name))) {
704 if (! (mask & KADM5_MOD_TIME))
706 if (! (mask & KADM5_MOD_NAME)) {
707 krb5_free_principal(handle->context, entry->principal);
708 entry->principal = NULL;
712 if (mask & KADM5_ATTRIBUTES)
713 entry->attributes = kdb.attributes;
715 if (mask & KADM5_KVNO)
716 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
717 if (kdb.key_data[i].key_data_kvno > entry->kvno)
718 entry->kvno = kdb.key_data[i].key_data_kvno;
720 if (handle->api_version == KADM5_API_VERSION_2)
723 /* XXX I'll be damned if I know how to deal with this one --marc */
728 * The new fields that only exist in version 2 start here
730 if (handle->api_version == KADM5_API_VERSION_2) {
731 if (mask & KADM5_MAX_RLIFE)
732 entry->max_renewable_life = kdb.max_renewable_life;
733 if (mask & KADM5_LAST_SUCCESS)
734 entry->last_success = kdb.last_success;
735 if (mask & KADM5_LAST_FAILED)
736 entry->last_failed = kdb.last_failed;
737 if (mask & KADM5_FAIL_AUTH_COUNT)
738 entry->fail_auth_count = kdb.fail_auth_count;
739 if (mask & KADM5_TL_DATA) {
740 krb5_tl_data *tl, *tl2;
742 entry->tl_data = NULL;
746 if (tl->tl_data_type > 255) {
747 if ((tl2 = dup_tl_data(tl)) == NULL) {
751 tl2->tl_data_next = entry->tl_data;
752 entry->tl_data = tl2;
756 tl = tl->tl_data_next;
759 if (mask & KADM5_KEY_DATA) {
760 entry->n_key_data = kdb.n_key_data;
761 if(entry->n_key_data) {
762 entry->key_data = (krb5_key_data *)
763 malloc(entry->n_key_data*sizeof(krb5_key_data));
764 if (entry->key_data == NULL) {
769 entry->key_data = NULL;
771 for (i = 0; i < entry->n_key_data; i++)
772 if (ret = krb5_copy_key_data_contents(handle->context,
774 &entry->key_data[i]))
780 * If KADM5_API_VERSION_1, we return an allocated structure, and
781 * we need to convert the new structure back into the format the
782 * caller is expecting.
784 if (handle->api_version == KADM5_API_VERSION_1) {
785 kadm5_principal_ent_t_v1 newv1;
787 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
793 newv1->principal = entry->principal;
794 newv1->princ_expire_time = entry->princ_expire_time;
795 newv1->last_pwd_change = entry->last_pwd_change;
796 newv1->pw_expiration = entry->pw_expiration;
797 newv1->max_life = entry->max_life;
798 newv1->mod_name = entry->mod_name;
799 newv1->mod_date = entry->mod_date;
800 newv1->attributes = entry->attributes;
801 newv1->kvno = entry->kvno;
802 newv1->mkvno = entry->mkvno;
803 newv1->policy = entry->policy;
804 newv1->aux_attributes = entry->aux_attributes;
806 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
812 if (ret && entry->principal)
813 krb5_free_principal(handle->context, entry->principal);
814 kdb_free_entry(handle, &kdb, &adb);
820 * Function: check_pw_reuse
822 * Purpose: Check if a key appears in a list of keys, in order to
823 * enforce password history.
827 * context (r) the krb5 context
828 * hist_keyblock (r) the key that hist_key_data is
830 * n_new_key_data (r) length of new_key_data
831 * new_key_data (r) keys to check against
832 * pw_hist_data, encrypted in hist_keyblock
833 * n_pw_hist_data (r) length of pw_hist_data
834 * pw_hist_data (r) passwords to check new_key_data against
837 * For each new_key in new_key_data:
838 * decrypt new_key with the master_keyblock
839 * for each password in pw_hist_data:
840 * for each hist_key in password:
841 * decrypt hist_key with hist_keyblock
842 * compare the new_key and hist_key
844 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
845 * new_key_data is the same as a key in pw_hist_data, or 0.
848 check_pw_reuse(krb5_context context,
849 krb5_keyblock *hist_keyblock,
850 int n_new_key_data, krb5_key_data *new_key_data,
851 int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
854 krb5_keyblock newkey, histkey;
857 for (x = 0; x < n_new_key_data; x++) {
858 if (ret = krb5_dbekd_decrypt_key_data(context,
863 for (y = 0; y < n_pw_hist_data; y++) {
864 for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
866 krb5_dbekd_decrypt_key_data(context,
868 &pw_hist_data[y].key_data[z],
872 if ((newkey.length == histkey.length) &&
873 (newkey.enctype == histkey.enctype) &&
874 (memcmp(newkey.contents, histkey.contents,
875 histkey.length) == 0)) {
876 krb5_free_keyblock_contents(context, &histkey);
877 krb5_free_keyblock_contents(context, &newkey);
879 return(KADM5_PASS_REUSE);
881 krb5_free_keyblock_contents(context, &histkey);
884 krb5_free_keyblock_contents(context, &newkey);
891 * Function: create_history_entry
893 * Purpose: Creates a password history entry from an array of
898 * context (r) krb5_context to use
899 * n_key_data (r) number of elements in key_data
900 * key_data (r) keys to add to the history entry
901 * hist (w) history entry to fill in
905 * hist->key_data is allocated to store n_key_data key_datas. Each
906 * element of key_data is decrypted with master_keyblock, re-encrypted
907 * in hist_key, and added to hist->key_data. hist->n_key_data is
911 int create_history_entry(krb5_context context, int n_key_data,
912 krb5_key_data *key_data, osa_pw_hist_ent *hist)
918 hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
919 if (hist->key_data == NULL)
921 memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
923 for (i = 0; i < n_key_data; i++) {
924 if (ret = krb5_dbekd_decrypt_key_data(context,
929 if (ret = krb5_dbekd_encrypt_key_data(context,
932 key_data[i].key_data_kvno,
935 krb5_free_keyblock_contents(context, &key);
936 /* krb5_free_keysalt(context, &salt); */
939 hist->n_key_data = n_key_data;
944 int free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
948 for (i = 0; i < hist->n_key_data; i++)
949 krb5_free_key_data_contents(context, &hist->key_data[i]);
950 free(hist->key_data);
954 * Function: add_to_history
956 * Purpose: Adds a password to a principal's password history.
960 * context (r) krb5_context to use
961 * adb (r/w) admin principal entry to add keys to
962 * pol (r) adb's policy
963 * pw (r) keys for the password to add to adb's key history
967 * add_to_history adds a single password to adb's password history.
968 * pw contains n_key_data keys in its key_data, in storage should be
969 * allocated but not freed by the caller (XXX blech!).
971 * This function maintains adb->old_keys as a circular queue. It
972 * starts empty, and grows each time this function is called until it
973 * is pol->pw_history_num items long. adb->old_key_len holds the
974 * number of allocated entries in the array, and must therefore be [0,
975 * pol->pw_history_num). adb->old_key_next is the index into the
976 * array where the next element should be written, and must be [0,
979 static kadm5_ret_t add_to_history(krb5_context context,
981 kadm5_policy_ent_t pol,
984 osa_pw_hist_ent *histp;
987 /* A history of 1 means just check the current password */
988 if (pol->pw_history_num == 1)
991 /* resize the adb->old_keys array if necessary */
992 if (adb->old_key_len < pol->pw_history_num-1) {
993 if (adb->old_keys == NULL) {
994 adb->old_keys = (osa_pw_hist_ent *)
995 malloc((adb->old_key_len + 1) * sizeof (osa_pw_hist_ent));
997 adb->old_keys = (osa_pw_hist_ent *)
998 realloc(adb->old_keys,
999 (adb->old_key_len + 1) * sizeof (osa_pw_hist_ent));
1001 if (adb->old_keys == NULL)
1004 memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent));
1008 /* free the old pw history entry if it contains data */
1009 histp = &adb->old_keys[adb->old_key_next];
1010 for (i = 0; i < histp->n_key_data; i++)
1011 krb5_free_key_data_contents(context, &histp->key_data[i]);
1013 /* store the new entry */
1014 adb->old_keys[adb->old_key_next] = *pw;
1016 /* update the next pointer */
1017 if (++adb->old_key_next == pol->pw_history_num-1)
1018 adb->old_key_next = 0;
1024 kadm5_chpass_principal(void *server_handle,
1025 krb5_principal principal, char *password)
1028 kadm5_chpass_principal_3(server_handle, principal, FALSE,
1033 kadm5_chpass_principal_3(void *server_handle,
1034 krb5_principal principal, krb5_boolean keepold,
1035 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1039 kadm5_policy_ent_rec pol;
1040 osa_princ_ent_rec adb;
1041 krb5_db_entry kdb, kdb_save;
1042 int ret, ret2, last_pwd, hist_added;
1044 kadm5_server_handle_t handle = server_handle;
1045 osa_pw_hist_ent hist;
1047 CHECK_HANDLE(server_handle);
1050 memset(&hist, 0, sizeof(hist));
1052 if (principal == NULL || password == NULL)
1054 if ((krb5_principal_compare(handle->context,
1055 principal, hist_princ)) == TRUE)
1056 return KADM5_PROTECT_PRINCIPAL;
1058 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1061 /* we are going to need the current keys after the new keys are set */
1062 if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1063 kdb_free_entry(handle, &kdb, &adb);
1067 if ((adb.aux_attributes & KADM5_POLICY)) {
1068 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1073 if ((ret = passwd_check(handle, password, adb.aux_attributes &
1074 KADM5_POLICY, &pol, principal)))
1077 if (ret = krb5_dbe_cpw(handle->context, &master_keyblock,
1078 n_ks_tuple?ks_tuple:handle->params.keysalts,
1079 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1080 password, 0 /* increment kvno */,
1084 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1086 if (ret = krb5_timeofday(handle->context, &now))
1089 if ((adb.aux_attributes & KADM5_POLICY)) {
1090 /* the policy was loaded before */
1092 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1098 * The spec says this check is overridden if the caller has
1099 * modify privilege. The admin server therefore makes this
1100 * check itself (in chpass_principal_wrapper, misc.c). A
1101 * local caller implicitly has all authorization bits.
1103 if ((now - last_pwd) < pol.pw_min_life &&
1104 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1105 ret = KADM5_PASS_TOOSOON;
1110 if (ret = create_history_entry(handle->context,
1111 kdb_save.n_key_data,
1112 kdb_save.key_data, &hist))
1115 if (ret = check_pw_reuse(handle->context,
1117 kdb.n_key_data, kdb.key_data,
1121 if (pol.pw_history_num > 1) {
1122 if (adb.admin_history_kvno != hist_kvno) {
1123 ret = KADM5_BAD_HIST_KEY;
1127 if (ret = check_pw_reuse(handle->context,
1129 kdb.n_key_data, kdb.key_data,
1130 adb.old_key_len, adb.old_keys))
1133 if (ret = add_to_history(handle->context, &adb, &pol, &hist))
1138 if (pol.pw_max_life)
1139 kdb.pw_expiration = now + pol.pw_max_life;
1141 kdb.pw_expiration = 0;
1143 kdb.pw_expiration = 0;
1146 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1149 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1154 if (!hist_added && hist.key_data)
1155 free_history_entry(handle->context, &hist);
1156 kdb_free_entry(handle, &kdb, &adb);
1157 kdb_free_entry(handle, &kdb_save, NULL);
1158 krb5_dbe_free_contents(handle->context, &kdb);
1160 if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1168 kadm5_randkey_principal(void *server_handle,
1169 krb5_principal principal,
1170 krb5_keyblock **keyblocks,
1174 kadm5_randkey_principal_3(server_handle, principal,
1179 kadm5_randkey_principal_3(void *server_handle,
1180 krb5_principal principal,
1181 krb5_boolean keepold,
1182 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1183 krb5_keyblock **keyblocks,
1187 osa_princ_ent_rec adb;
1189 kadm5_policy_ent_rec pol;
1190 krb5_key_data *key_data;
1191 int ret, last_pwd, have_pol = 0;
1192 kadm5_server_handle_t handle = server_handle;
1197 CHECK_HANDLE(server_handle);
1199 if (principal == NULL)
1201 if (hist_princ && /* this will be NULL when initializing the databse */
1202 ((krb5_principal_compare(handle->context,
1203 principal, hist_princ)) == TRUE))
1204 return KADM5_PROTECT_PRINCIPAL;
1206 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1209 if (ret = krb5_dbe_crk(handle->context, &master_keyblock,
1210 n_ks_tuple?ks_tuple:handle->params.keysalts,
1211 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1216 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1218 if (ret = krb5_timeofday(handle->context, &now))
1221 if ((adb.aux_attributes & KADM5_POLICY)) {
1222 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1227 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1233 * The spec says this check is overridden if the caller has
1234 * modify privilege. The admin server therefore makes this
1235 * check itself (in chpass_principal_wrapper, misc.c). A
1236 * local caller implicitly has all authorization bits.
1238 if((now - last_pwd) < pol.pw_min_life &&
1239 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1240 ret = KADM5_PASS_TOOSOON;
1245 if(pol.pw_history_num > 1) {
1246 if(adb.admin_history_kvno != hist_kvno) {
1247 ret = KADM5_BAD_HIST_KEY;
1251 if (ret = check_pw_reuse(handle->context,
1253 kdb.n_key_data, kdb.key_data,
1254 adb.old_key_len, adb.old_keys))
1257 if (pol.pw_max_life)
1258 kdb.pw_expiration = now + pol.pw_max_life;
1260 kdb.pw_expiration = 0;
1262 kdb.pw_expiration = 0;
1265 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1269 if (handle->api_version == KADM5_API_VERSION_1) {
1270 /* Version 1 clients will expect to see a DES_CRC enctype. */
1271 if (ret = krb5_dbe_find_enctype(handle->context, &kdb,
1272 ENCTYPE_DES_CBC_CRC,
1276 if (ret = decrypt_key_data(handle->context, 1, key_data,
1280 ret = decrypt_key_data(handle->context,
1281 kdb.n_key_data, kdb.key_data,
1288 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1293 kdb_free_entry(handle, &kdb, &adb);
1295 kadm5_free_policy_ent(handle->lhandle, &pol);
1301 * kadm5_setv4key_principal:
1303 * Set only ONE key of the principal, removing all others. This key
1304 * must have the DES_CBC_CRC enctype and is entered as having the
1305 * krb4 salttype. This is to enable things like kadmind4 to work.
1308 kadm5_setv4key_principal(void *server_handle,
1309 krb5_principal principal,
1310 krb5_keyblock *keyblock)
1313 osa_princ_ent_rec adb;
1315 kadm5_policy_ent_rec pol;
1316 krb5_keysalt keysalt;
1317 int i, kvno, ret, have_pol = 0;
1321 kadm5_server_handle_t handle = server_handle;
1323 CHECK_HANDLE(server_handle);
1325 if (principal == NULL || keyblock == NULL)
1327 if (hist_princ && /* this will be NULL when initializing the databse */
1328 ((krb5_principal_compare(handle->context,
1329 principal, hist_princ)) == TRUE))
1330 return KADM5_PROTECT_PRINCIPAL;
1332 if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1333 return KADM5_SETV4KEY_INVAL_ENCTYPE;
1335 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1338 for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1339 if (kdb.key_data[i].key_data_kvno > kvno)
1340 kvno = kdb.key_data[i].key_data_kvno;
1342 if (kdb.key_data != NULL)
1343 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1345 kdb.key_data = (krb5_key_data*)malloc(sizeof(krb5_key_data));
1346 if (kdb.key_data == NULL)
1348 memset(kdb.key_data, 0, sizeof(krb5_key_data));
1350 keysalt.type = KRB5_KDB_SALTTYPE_V4;
1351 /* XXX data.magic? */
1352 keysalt.data.length = 0;
1353 keysalt.data.data = NULL;
1355 if (ret = krb5_dbekd_encrypt_key_data(handle->context,
1363 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1365 if (ret = krb5_timeofday(handle->context, &now))
1368 if ((adb.aux_attributes & KADM5_POLICY)) {
1369 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1376 * The spec says this check is overridden if the caller has
1377 * modify privilege. The admin server therefore makes this
1378 * check itself (in chpass_principal_wrapper, misc.c). A
1379 * local caller implicitly has all authorization bits.
1381 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1384 if((now - last_pwd) < pol.pw_min_life &&
1385 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1386 ret = KADM5_PASS_TOOSOON;
1392 * Should we be checking/updating pw history here?
1394 if(pol.pw_history_num > 1) {
1395 if(adb.admin_history_kvno != hist_kvno) {
1396 ret = KADM5_BAD_HIST_KEY;
1400 if (ret = check_pw_reuse(handle->context,
1402 kdb.n_key_data, kdb.key_data,
1403 adb.old_key_len, adb.old_keys))
1408 if (pol.pw_max_life)
1409 kdb.pw_expiration = now + pol.pw_max_life;
1411 kdb.pw_expiration = 0;
1413 kdb.pw_expiration = 0;
1416 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1419 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1424 kdb_free_entry(handle, &kdb, &adb);
1426 kadm5_free_policy_ent(handle->lhandle, &pol);
1432 kadm5_setkey_principal(void *server_handle,
1433 krb5_principal principal,
1434 krb5_keyblock *keyblocks,
1438 kadm5_setkey_principal_3(server_handle, principal,
1444 kadm5_setkey_principal_3(void *server_handle,
1445 krb5_principal principal,
1446 krb5_boolean keepold,
1447 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1448 krb5_keyblock *keyblocks,
1452 osa_princ_ent_rec adb;
1454 kadm5_policy_ent_rec pol;
1455 krb5_key_data *old_key_data;
1457 int i, j, kvno, ret, have_pol = 0;
1461 kadm5_server_handle_t handle = server_handle;
1462 krb5_boolean similar;
1463 krb5_keysalt keysalt;
1465 CHECK_HANDLE(server_handle);
1467 if (principal == NULL || keyblocks == NULL)
1469 if (hist_princ && /* this will be NULL when initializing the databse */
1470 ((krb5_principal_compare(handle->context,
1471 principal, hist_princ)) == TRUE))
1472 return KADM5_PROTECT_PRINCIPAL;
1474 for (i = 0; i < n_keys; i++) {
1475 for (j = i+1; j < n_keys; j++) {
1476 if ((ret = krb5_c_enctype_compare(handle->context,
1477 keyblocks[i].enctype,
1478 keyblocks[j].enctype,
1483 if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1484 return KADM5_SETKEY_DUP_ENCTYPES;
1486 return KADM5_SETKEY_DUP_ENCTYPES;
1491 if (n_ks_tuple != n_keys)
1492 return KADM5_SETKEY3_ETYPE_MISMATCH;
1494 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1497 for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1498 if (kdb.key_data[i].key_data_kvno > kvno)
1499 kvno = kdb.key_data[i].key_data_kvno;
1502 old_key_data = kdb.key_data;
1503 n_old_keys = kdb.n_key_data;
1505 if (kdb.key_data != NULL)
1506 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1508 old_key_data = NULL;
1511 kdb.key_data = (krb5_key_data*)malloc((n_keys+n_old_keys)
1512 *sizeof(krb5_key_data));
1513 if (kdb.key_data == NULL)
1515 memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1518 for (i = 0; i < n_keys; i++) {
1520 keysalt.type = ks_tuple[i].ks_salttype;
1521 keysalt.data.length = 0;
1522 keysalt.data.data = NULL;
1523 if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1524 cleanup_key_data(handle->context, kdb.n_key_data,
1526 return KADM5_SETKEY3_ETYPE_MISMATCH;
1529 ret = krb5_dbekd_encrypt_key_data(handle->context,
1532 n_ks_tuple ? &keysalt : NULL,
1536 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1542 /* copy old key data if necessary */
1543 for (i = 0; i < n_old_keys; i++) {
1544 kdb.key_data[i+n_keys] = old_key_data[i];
1545 memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1548 /* assert(kdb.n_key_data == n_keys + n_old_keys) */
1549 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1551 if ((ret = krb5_timeofday(handle->context, &now)))
1554 if ((adb.aux_attributes & KADM5_POLICY)) {
1555 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1562 * The spec says this check is overridden if the caller has
1563 * modify privilege. The admin server therefore makes this
1564 * check itself (in chpass_principal_wrapper, misc.c). A
1565 * local caller implicitly has all authorization bits.
1567 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1570 if((now - last_pwd) < pol.pw_min_life &&
1571 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1572 ret = KADM5_PASS_TOOSOON;
1578 * Should we be checking/updating pw history here?
1580 if(pol.pw_history_num > 1) {
1581 if(adb.admin_history_kvno != hist_kvno) {
1582 ret = KADM5_BAD_HIST_KEY;
1586 if (ret = check_pw_reuse(handle->context,
1588 kdb.n_key_data, kdb.key_data,
1589 adb.old_key_len, adb.old_keys))
1594 if (pol.pw_max_life)
1595 kdb.pw_expiration = now + pol.pw_max_life;
1597 kdb.pw_expiration = 0;
1599 kdb.pw_expiration = 0;
1602 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
1605 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1610 kdb_free_entry(handle, &kdb, &adb);
1612 kadm5_free_policy_ent(handle->lhandle, &pol);
1618 * Allocate an array of n_key_data krb5_keyblocks, fill in each
1619 * element with the results of decrypting the nth key in key_data with
1620 * master_keyblock, and if n_keys is not NULL fill it in with the
1621 * number of keys decrypted.
1623 static int decrypt_key_data(krb5_context context,
1624 int n_key_data, krb5_key_data *key_data,
1625 krb5_keyblock **keyblocks, int *n_keys)
1627 krb5_keyblock *keys;
1630 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1633 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1635 for (i = 0; i < n_key_data; i++) {
1636 ret = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
1641 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1649 *n_keys = n_key_data;
1655 * Function: kadm5_decrypt_key
1657 * Purpose: Retrieves and decrypts a principal key.
1661 * server_handle (r) kadm5 handle
1662 * entry (r) principal retrieved with kadm5_get_principal
1663 * ktype (r) enctype to search for, or -1 to ignore
1664 * stype (r) salt type to search for, or -1 to ignore
1665 * kvno (r) kvno to search for, -1 for max, 0 for max
1666 * only if it also matches ktype and stype
1667 * keyblock (w) keyblock to fill in
1668 * keysalt (w) keysalt to fill in, or NULL
1669 * kvnop (w) kvno to fill in, or NULL
1671 * Effects: Searches the key_data array of entry, which must have been
1672 * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1673 * find a key with a specified enctype, salt type, and kvno in a
1674 * principal entry. If not found, return ENOENT. Otherwise, decrypt
1675 * it with the master key, and return the key in keyblock, the salt
1676 * in salttype, and the key version number in kvno.
1678 * If ktype or stype is -1, it is ignored for the search. If kvno is
1679 * -1, ktype and stype are ignored and the key with the max kvno is
1680 * returned. If kvno is 0, only the key with the max kvno is returned
1681 * and only if it matches the ktype and stype; otherwise, ENOENT is
1684 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1685 kadm5_principal_ent_t entry, krb5_int32
1686 ktype, krb5_int32 stype, krb5_int32
1687 kvno, krb5_keyblock *keyblock,
1688 krb5_keysalt *keysalt, int *kvnop)
1690 kadm5_server_handle_t handle = server_handle;
1691 krb5_db_entry dbent;
1692 krb5_key_data *key_data;
1695 CHECK_HANDLE(server_handle);
1697 if (entry->n_key_data == 0 || entry->key_data == NULL)
1700 /* find_enctype only uses these two fields */
1701 dbent.n_key_data = entry->n_key_data;
1702 dbent.key_data = entry->key_data;
1703 if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1704 stype, kvno, &key_data)))
1707 if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
1708 &master_keyblock, key_data,
1709 keyblock, keysalt)))
1713 *kvnop = key_data->key_data_kvno;