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_encrypt_block master_encblock;
26 extern krb5_encrypt_block hist_encblock;
27 extern krb5_keyblock master_keyblock;
28 extern krb5_keyblock hist_key;
29 extern krb5_db_entry master_db;
30 extern krb5_db_entry hist_db;
31 extern krb5_kvno hist_kvno;
33 static int decrypt_key_data(krb5_context context,
34 int n_key_data, krb5_key_data *key_data,
35 krb5_keyblock **keyblocks, int *n_keys);
38 * XXX Functions that ought to be in libkrb5.a, but aren't.
40 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
42 krb5_key_data *from, *to;
48 idx = (from->key_data_ver == 1 ? 1 : 2);
50 for (i = 0; i < idx; i++) {
51 if ( from->key_data_length[i] ) {
52 to->key_data_contents[i] = malloc(from->key_data_length[i]);
53 if (to->key_data_contents[i] == NULL) {
54 for (i = 0; i < idx; i++) {
55 if (to->key_data_contents[i]) {
56 memset(to->key_data_contents[i], 0,
57 to->key_data_length[i]);
58 free(to->key_data_contents[i]);
63 memcpy(to->key_data_contents[i], from->key_data_contents[i],
64 from->key_data_length[i]);
70 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
74 n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
77 n->tl_data_contents = malloc(tl->tl_data_length);
78 if (n->tl_data_contents == NULL) {
82 memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
83 n->tl_data_type = tl->tl_data_type;
84 n->tl_data_length = tl->tl_data_length;
85 n->tl_data_next = NULL;
90 kadm5_create_principal(void *server_handle,
91 kadm5_principal_ent_t entry, long mask,
95 osa_princ_ent_rec adb;
96 kadm5_policy_ent_rec polent;
98 krb5_tl_data *tl_data_orig, *tl_data_tail;
100 kadm5_server_handle_t handle = server_handle;
102 CHECK_HANDLE(server_handle);
105 * Argument sanity checking, and opening up the DB
107 if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
108 (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
109 (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
110 (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
111 (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
112 (mask & KADM5_FAIL_AUTH_COUNT))
113 return KADM5_BAD_MASK;
114 if((mask & ~ALL_PRINC_MASK))
115 return KADM5_BAD_MASK;
116 if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
120 * Check to see if the principal exists
122 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
125 case KADM5_UNK_PRINC:
128 kdb_free_entry(handle, &kdb, &adb);
134 memset(&kdb, 0, sizeof(krb5_db_entry));
135 memset(&adb, 0, sizeof(osa_princ_ent_rec));
138 * If a policy was specified, load it.
139 * If we can not find the one specified return an error
141 if ((mask & KADM5_POLICY)) {
142 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
143 &polent)) != KADM5_OK) {
145 return KADM5_BAD_POLICY;
150 if (ret = passwd_check(handle, password, (mask & KADM5_POLICY),
151 &polent, entry->principal)) {
152 if (mask & KADM5_POLICY)
153 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
157 * Start populating the various DB fields, using the
158 * "defaults" for fields that were not specified by the
161 if (ret = krb5_timeofday(handle->context, &now)) {
162 if (mask & KADM5_POLICY)
163 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
167 kdb.magic = KRB5_KDB_MAGIC_NUMBER;
168 kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
170 if ((mask & KADM5_ATTRIBUTES))
171 kdb.attributes = entry->attributes;
173 kdb.attributes = handle->params.flags;
175 if ((mask & KADM5_MAX_LIFE))
176 kdb.max_life = entry->max_life;
178 kdb.max_life = handle->params.max_life;
180 if (mask & KADM5_MAX_RLIFE)
181 kdb.max_renewable_life = entry->max_renewable_life;
183 kdb.max_renewable_life = handle->params.max_rlife;
185 if ((mask & KADM5_PRINC_EXPIRE_TIME))
186 kdb.expiration = entry->princ_expire_time;
188 kdb.expiration = handle->params.expiration;
190 kdb.pw_expiration = 0;
191 if ((mask & KADM5_POLICY)) {
192 if(polent.pw_max_life)
193 kdb.pw_expiration = now + polent.pw_max_life;
195 kdb.pw_expiration = 0;
197 if ((mask & KADM5_PW_EXPIRATION))
198 kdb.pw_expiration = entry->pw_expiration;
200 kdb.last_success = 0;
202 kdb.fail_auth_count = 0;
204 /* this is kind of gross, but in order to free the tl data, I need
205 to free the entire kdb entry, and that will try to free the
208 if (ret = krb5_copy_principal(handle->context,
209 entry->principal, &(kdb.princ))) {
210 if (mask & KADM5_POLICY)
211 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
215 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) {
216 krb5_dbe_free_contents(handle->context, &kdb);
217 if (mask & KADM5_POLICY)
218 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
222 /* initialize the keys */
224 if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
225 handle->params.keysalts,
226 handle->params.num_keysalts,
228 (mask & KADM5_KVNO)?entry->kvno:1, &kdb)) {
229 krb5_dbe_free_contents(handle->context, &kdb);
230 if (mask & KADM5_POLICY)
231 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
235 /* populate the admin-server-specific fields. In the OV server,
236 this used to be in a separate database. Since there's already
237 marshalling code for the admin fields, to keep things simple,
238 I'm going to keep it, and make all the admin stuff occupy a
239 single tl_data record, */
241 adb.admin_history_kvno = hist_kvno;
242 if ((mask & KADM5_POLICY)) {
243 adb.aux_attributes = KADM5_POLICY;
245 /* this does *not* need to be strdup'ed, because adb is xdr */
246 /* encoded in osa_adb_create_princ, and not ever freed */
248 adb.policy = entry->policy;
251 /* increment the policy ref count, if any */
253 if ((mask & KADM5_POLICY)) {
254 polent.policy_refcnt++;
255 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
258 krb5_dbe_free_contents(handle->context, &kdb);
259 if (mask & KADM5_POLICY)
260 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
265 if (mask & KADM5_TL_DATA) {
266 /* splice entry->tl_data onto the front of kdb.tl_data */
267 tl_data_orig = kdb.tl_data;
268 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
269 tl_data_tail = tl_data_tail->tl_data_next)
271 tl_data_tail->tl_data_next = kdb.tl_data;
272 kdb.tl_data = entry->tl_data;
275 /* store the new db entry */
276 ret = kdb_put_entry(handle, &kdb, &adb);
278 if (mask & KADM5_TL_DATA) {
279 /* remove entry->tl_data from the front of kdb.tl_data */
280 tl_data_tail->tl_data_next = NULL;
281 kdb.tl_data = tl_data_orig;
284 krb5_dbe_free_contents(handle->context, &kdb);
287 if ((mask & KADM5_POLICY)) {
288 /* decrement the policy ref count */
290 polent.policy_refcnt--;
292 * if this fails, there's nothing we can do anyway. the
293 * policy refcount wil be too high.
295 (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
299 if (mask & KADM5_POLICY)
300 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
304 if (mask & KADM5_POLICY)
305 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
312 kadm5_delete_principal(void *server_handle, krb5_principal principal)
315 kadm5_policy_ent_rec polent;
317 osa_princ_ent_rec adb;
318 kadm5_server_handle_t handle = server_handle;
320 CHECK_HANDLE(server_handle);
322 if (principal == NULL)
325 if (ret = kdb_get_entry(handle, principal, &kdb, &adb))
328 if ((adb.aux_attributes & KADM5_POLICY)) {
329 if ((ret = kadm5_get_policy(handle->lhandle,
330 adb.policy, &polent))
332 polent.policy_refcnt--;
333 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
336 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
337 kdb_free_entry(handle, &kdb, &adb);
341 if (ret = kadm5_free_policy_ent(handle->lhandle, &polent)) {
342 kdb_free_entry(handle, &kdb, &adb);
347 ret = kdb_delete_entry(handle, principal);
349 kdb_free_entry(handle, &kdb, &adb);
355 kadm5_modify_principal(void *server_handle,
356 kadm5_principal_ent_t entry, long mask)
359 kadm5_policy_ent_rec npol, opol;
360 int have_npol = 0, have_opol = 0;
362 krb5_tl_data *tl_data_orig, *tl_data_tail;
363 osa_princ_ent_rec adb;
364 kadm5_server_handle_t handle = server_handle;
366 CHECK_HANDLE(server_handle);
368 if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
369 (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
370 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
371 (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
372 (mask & KADM5_LAST_FAILED))
373 return KADM5_BAD_MASK;
374 if((mask & ~ALL_PRINC_MASK))
375 return KADM5_BAD_MASK;
376 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
377 return KADM5_BAD_MASK;
378 if(entry == (kadm5_principal_ent_t) NULL)
380 if (mask & KADM5_TL_DATA) {
381 tl_data_orig = entry->tl_data;
382 while (tl_data_orig) {
383 if (tl_data_orig->tl_data_type < 256)
384 return KADM5_BAD_TL_TYPE;
385 tl_data_orig = tl_data_orig->tl_data_next;
389 if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb))
393 * This is pretty much the same as create ...
396 if ((mask & KADM5_POLICY)) {
397 /* get the new policy */
398 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
402 ret = KADM5_BAD_POLICY;
404 case KADM5_UNK_POLICY:
405 case KADM5_BAD_POLICY:
406 ret = KADM5_UNK_POLICY;
413 /* if we already have a policy, get it to decrement the refcnt */
414 if(adb.aux_attributes & KADM5_POLICY) {
415 /* ... but not if the old and new are the same */
416 if(strcmp(adb.policy, entry->policy)) {
417 ret = kadm5_get_policy(handle->lhandle,
421 case KADM5_BAD_POLICY:
422 case KADM5_UNK_POLICY:
426 opol.policy_refcnt--;
432 npol.policy_refcnt++;
434 } else npol.policy_refcnt++;
436 /* set us up to use the new policy */
437 adb.aux_attributes |= KADM5_POLICY;
440 adb.policy = strdup(entry->policy);
442 /* set pw_max_life based on new policy */
443 if (npol.pw_max_life) {
444 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
445 &(kdb.pw_expiration)))
447 kdb.pw_expiration += npol.pw_max_life;
449 kdb.pw_expiration = 0;
453 if ((mask & KADM5_POLICY_CLR) &&
454 (adb.aux_attributes & KADM5_POLICY)) {
455 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
458 case KADM5_BAD_POLICY:
459 case KADM5_UNK_POLICY:
468 adb.aux_attributes &= ~KADM5_POLICY;
469 kdb.pw_expiration = 0;
470 opol.policy_refcnt--;
478 if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
481 kadm5_modify_policy_internal(handle->lhandle, &opol,
482 KADM5_REF_COUNT))) ||
485 kadm5_modify_policy_internal(handle->lhandle, &npol,
489 if ((mask & KADM5_ATTRIBUTES))
490 kdb.attributes = entry->attributes;
491 if ((mask & KADM5_MAX_LIFE))
492 kdb.max_life = entry->max_life;
493 if ((mask & KADM5_PRINC_EXPIRE_TIME))
494 kdb.expiration = entry->princ_expire_time;
495 if (mask & KADM5_PW_EXPIRATION)
496 kdb.pw_expiration = entry->pw_expiration;
497 if (mask & KADM5_MAX_RLIFE)
498 kdb.max_renewable_life = entry->max_renewable_life;
499 if (mask & KADM5_FAIL_AUTH_COUNT)
500 kdb.fail_auth_count = entry->fail_auth_count;
502 if((mask & KADM5_KVNO)) {
503 for (i = 0; i < kdb.n_key_data; i++)
504 kdb.key_data[i].key_data_kvno = entry->kvno;
507 if (mask & KADM5_TL_DATA) {
510 * Replace kdb.tl_data with what was passed in. The
511 * KRB5_TL_KADM_DATA will be re-added (based on adb) by
512 * kdb_put_entry, below.
514 while (kdb.tl_data) {
515 tl = kdb.tl_data->tl_data_next;
516 free(kdb.tl_data->tl_data_contents);
521 kdb.tl_data = entry->tl_data;
522 kdb.n_tl_data = entry->n_tl_data;
525 ret = kdb_put_entry(handle, &kdb, &adb);
526 if (mask & KADM5_TL_DATA) {
527 /* prevent kdb_free_entry from freeing the caller's data */
536 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
537 ret = ret ? ret : ret2;
540 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
541 ret = ret ? ret : ret2;
543 kdb_free_entry(handle, &kdb, &adb);
548 kadm5_rename_principal(void *server_handle,
549 krb5_principal source, krb5_principal target)
552 osa_princ_ent_rec adb;
554 kadm5_server_handle_t handle = server_handle;
556 CHECK_HANDLE(server_handle);
558 if (source == NULL || target == NULL)
561 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
562 kdb_free_entry(handle, &kdb, &adb);
566 if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
569 /* this is kinda gross, but unavoidable */
571 for (i=0; i<kdb.n_key_data; i++) {
572 if ((kdb.key_data[i].key_data_ver == 1) ||
573 (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
574 ret = KADM5_NO_RENAME_SALT;
579 krb5_free_principal(handle->context, kdb.princ);
580 if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) {
581 kdb.princ = NULL; /* so freeing the dbe doesn't lose */
585 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
588 ret = kdb_delete_entry(handle, source);
591 kdb_free_entry(handle, &kdb, &adb);
596 kadm5_get_principal(void *server_handle, krb5_principal principal,
597 kadm5_principal_ent_t entry,
601 osa_princ_ent_rec adb;
602 osa_adb_ret_t ret = 0;
605 kadm5_server_handle_t handle = server_handle;
606 kadm5_principal_ent_rec entry_local, *entry_orig;
608 CHECK_HANDLE(server_handle);
611 * In version 1, all the defined fields are always returned.
612 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
613 * filled with allocated memory.
615 if (handle->api_version == KADM5_API_VERSION_1) {
616 mask = KADM5_PRINCIPAL_NORMAL_MASK;
618 entry = &entry_local;
623 memset((char *) entry, 0, sizeof(*entry));
625 if (principal == NULL)
628 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
631 if ((mask & KADM5_POLICY) &&
632 adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
633 if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
637 strcpy(entry->policy, adb.policy);
640 if (mask & KADM5_AUX_ATTRIBUTES)
641 entry->aux_attributes = adb.aux_attributes;
643 if ((mask & KADM5_PRINCIPAL) &&
644 (ret = krb5_copy_principal(handle->context, principal,
645 &entry->principal))) {
649 if (mask & KADM5_PRINC_EXPIRE_TIME)
650 entry->princ_expire_time = kdb.expiration;
652 if ((mask & KADM5_LAST_PWD_CHANGE) &&
653 (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
654 &(entry->last_pwd_change)))) {
658 if (mask & KADM5_PW_EXPIRATION)
659 entry->pw_expiration = kdb.pw_expiration;
660 if (mask & KADM5_MAX_LIFE)
661 entry->max_life = kdb.max_life;
663 /* this is a little non-sensical because the function returns two */
664 /* values that must be checked separately against the mask */
665 if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
666 if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
668 &(entry->mod_name))) {
671 if (! (mask & KADM5_MOD_TIME))
673 if (! (mask & KADM5_MOD_NAME)) {
674 krb5_free_principal(handle->context, entry->principal);
675 entry->principal = NULL;
679 if (mask & KADM5_ATTRIBUTES)
680 entry->attributes = kdb.attributes;
682 if (mask & KADM5_KVNO)
683 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
684 if (kdb.key_data[i].key_data_kvno > entry->kvno)
685 entry->kvno = kdb.key_data[i].key_data_kvno;
687 if (handle->api_version == KADM5_API_VERSION_2)
690 /* XXX I'll be damned if I know how to deal with this one --marc */
695 * The new fields that only exist in version 2 start here
697 if (handle->api_version == KADM5_API_VERSION_2) {
698 if (mask & KADM5_MAX_RLIFE)
699 entry->max_renewable_life = kdb.max_renewable_life;
700 if (mask & KADM5_LAST_SUCCESS)
701 entry->last_success = kdb.last_success;
702 if (mask & KADM5_LAST_FAILED)
703 entry->last_failed = kdb.last_failed;
704 if (mask & KADM5_FAIL_AUTH_COUNT)
705 entry->fail_auth_count = kdb.fail_auth_count;
706 if (mask & KADM5_TL_DATA) {
707 krb5_tl_data td, *tl, *tl2;
709 entry->tl_data = NULL;
713 if (tl->tl_data_type > 255) {
714 if ((tl2 = dup_tl_data(tl)) == NULL) {
718 tl2->tl_data_next = entry->tl_data;
719 entry->tl_data = tl2;
723 tl = tl->tl_data_next;
726 if (mask & KADM5_KEY_DATA) {
727 entry->n_key_data = kdb.n_key_data;
728 if(entry->n_key_data) {
729 entry->key_data = (krb5_key_data *)
730 malloc(entry->n_key_data*sizeof(krb5_key_data));
731 if (entry->key_data == NULL) {
736 entry->key_data = NULL;
738 for (i = 0; i < entry->n_key_data; i++)
739 if (ret = krb5_copy_key_data_contents(handle->context,
741 &entry->key_data[i]))
747 * If KADM5_API_VERSION_1, we return an allocated structure, and
748 * we need to convert the new structure back into the format the
749 * caller is expecting.
751 if (handle->api_version == KADM5_API_VERSION_1) {
752 kadm5_principal_ent_t_v1 newv1;
754 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
760 newv1->principal = entry->principal;
761 newv1->princ_expire_time = entry->princ_expire_time;
762 newv1->last_pwd_change = entry->last_pwd_change;
763 newv1->pw_expiration = entry->pw_expiration;
764 newv1->max_life = entry->max_life;
765 newv1->mod_name = entry->mod_name;
766 newv1->mod_date = entry->mod_date;
767 newv1->attributes = entry->attributes;
768 newv1->kvno = entry->kvno;
769 newv1->mkvno = entry->mkvno;
770 newv1->policy = entry->policy;
771 newv1->aux_attributes = entry->aux_attributes;
773 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
779 if (ret && entry->principal)
780 krb5_free_principal(handle->context, entry->principal);
781 kdb_free_entry(handle, &kdb, &adb);
787 * Function: check_pw_reuse
789 * Purpose: Check if a key appears in a list of keys, in order to
790 * enforce password history.
794 * context (r) the krb5 context
795 * histkey_encblock (r) the encblock that hist_key_data is
797 * n_new_key_data (r) length of new_key_data
798 * new_key_data (r) keys to check against
799 * pw_hist_data, encrypted in histkey_encblock
800 * n_pw_hist_data (r) length of pw_hist_data
801 * pw_hist_data (r) passwords to check new_key_data against
804 * For each new_key in new_key_data:
805 * decrypt new_key with the master_encblock
806 * for each password in pw_hist_data:
807 * for each hist_key in password:
808 * decrypt hist_key with histkey_encblock
809 * compare the new_key and hist_key
811 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
812 * new_key_data is the same as a key in pw_hist_data, or 0.
815 check_pw_reuse(krb5_context context,
816 krb5_encrypt_block *histkey_encblock,
817 int n_new_key_data, krb5_key_data *new_key_data,
818 int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
821 krb5_keyblock newkey, histkey;
824 for (x = 0; x < n_new_key_data; x++) {
825 if (ret = krb5_dbekd_decrypt_key_data(context,
830 for (y = 0; y < n_pw_hist_data; y++) {
831 for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
833 krb5_dbekd_decrypt_key_data(context,
835 &pw_hist_data[y].key_data[z],
839 if ((newkey.length == histkey.length) &&
840 (newkey.enctype == histkey.enctype) &&
841 (memcmp(newkey.contents, histkey.contents,
842 histkey.length) == 0)) {
843 krb5_free_keyblock_contents(context, &histkey);
844 krb5_free_keyblock_contents(context, &newkey);
846 return(KADM5_PASS_REUSE);
848 krb5_free_keyblock_contents(context, &histkey);
851 krb5_free_keyblock_contents(context, &newkey);
858 * Function: create_history_entry
860 * Purpose: Creates a password history entry from an array of
865 * context (r) krb5_context to use
866 * n_key_data (r) number of elements in key_data
867 * key_data (r) keys to add to the history entry
868 * hist (w) history entry to fill in
872 * hist->key_data is allocated to store n_key_data key_datas. Each
873 * element of key_data is decrypted with master_encblock, re-encrypted
874 * in hist_encblock, and added to hist->key_data. hist->n_key_data is
877 int create_history_entry(krb5_context context, int n_key_data,
878 krb5_key_data *key_data, osa_pw_hist_ent *hist)
884 hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
885 if (hist->key_data == NULL)
887 memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
889 for (i = 0; i < n_key_data; i++) {
890 if (ret = krb5_dbekd_decrypt_key_data(context,
895 if (ret = krb5_dbekd_encrypt_key_data(context,
898 key_data[i].key_data_kvno,
901 krb5_free_keyblock_contents(context, &key);
902 /* krb5_free_keysalt(context, &salt); */
905 hist->n_key_data = n_key_data;
909 int free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
913 for (i = 0; i < hist->n_key_data; i++)
914 krb5_free_key_data_contents(context, &hist->key_data[i]);
915 free(hist->key_data);
919 * Function: add_to_history
921 * Purpose: Adds a password to a principal's password history.
925 * context (r) krb5_context to use
926 * adb (r/w) admin principal entry to add keys to
927 * pol (r) adb's policy
928 * pw (r) keys for the password to add to adb's key history
932 * add_to_history adds a single password to adb's password history.
933 * pw contains n_key_data keys in its key_data, in storage should be
934 * allocated but not freed by the caller (XXX blech!).
936 * This function maintains adb->old_keys as a circular queue. It
937 * starts empty, and grows each time this function is called until it
938 * is pol->pw_history_num items long. adb->old_key_len holds the
939 * number of allocated entries in the array, and must therefore be [0,
940 * pol->pw_history_num). adb->old_key_next is the index into the
941 * array where the next element should be written, and must be [0,
944 static kadm5_ret_t add_to_history(krb5_context context,
946 kadm5_policy_ent_t pol,
949 osa_pw_hist_ent hist, *histp;
952 /* A history of 1 means just check the current password */
953 if (pol->pw_history_num == 1)
956 /* resize the adb->old_keys array if necessary */
957 if (adb->old_key_len < pol->pw_history_num-1) {
958 adb->old_keys = (osa_pw_hist_ent *)
959 realloc(adb->old_keys,
960 (adb->old_key_len+1)*sizeof(osa_pw_hist_ent));
961 if (adb->old_keys == NULL)
964 memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent));
968 /* free the old pw history entry if it contains data */
969 histp = &adb->old_keys[adb->old_key_next];
970 for (i = 0; i < histp->n_key_data; i++)
971 krb5_free_key_data_contents(context, &histp->key_data[i]);
973 /* store the new entry */
974 adb->old_keys[adb->old_key_next] = *pw;
976 /* update the next pointer */
977 if (++adb->old_key_next == pol->pw_history_num-1)
978 adb->old_key_next = 0;
984 kadm5_chpass_principal(void *server_handle,
985 krb5_principal principal, char *password)
988 kadm5_policy_ent_rec pol;
989 osa_princ_ent_rec adb;
990 krb5_db_entry kdb, kdb_save;
991 int ret, ret2, last_pwd, i, hist_added;
993 kadm5_server_handle_t handle = server_handle;
994 osa_pw_hist_ent hist;
996 CHECK_HANDLE(server_handle);
999 memset(&hist, 0, sizeof(hist));
1001 if (principal == NULL || password == NULL)
1003 if ((krb5_principal_compare(handle->context,
1004 principal, hist_princ)) == TRUE)
1005 return KADM5_PROTECT_PRINCIPAL;
1007 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1010 /* we are going to need the current keys after the new keys are set */
1011 if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1012 kdb_free_entry(handle, &kdb, &adb);
1016 if ((adb.aux_attributes & KADM5_POLICY)) {
1017 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1022 if ((ret = passwd_check(handle, password, adb.aux_attributes &
1023 KADM5_POLICY, &pol, principal)))
1026 if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
1027 handle->params.keysalts,
1028 handle->params.num_keysalts,
1029 password, 0 /* increment kvno */, &kdb))
1032 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1034 if (ret = krb5_timeofday(handle->context, &now))
1037 if ((adb.aux_attributes & KADM5_POLICY)) {
1038 /* the policy was loaded before */
1040 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1046 * The spec says this check is overridden if the caller has
1047 * modify privilege. The admin server therefore makes this
1048 * check itself (in chpass_principal_wrapper, misc.c). A
1049 * local caller implicitly has all authorization bits.
1051 if ((now - last_pwd) < pol.pw_min_life &&
1052 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1053 ret = KADM5_PASS_TOOSOON;
1058 if (ret = create_history_entry(handle->context,
1059 kdb_save.n_key_data,
1060 kdb_save.key_data, &hist))
1063 if (ret = check_pw_reuse(handle->context,
1065 kdb.n_key_data, kdb.key_data,
1069 if (pol.pw_history_num > 1) {
1070 if (adb.admin_history_kvno != hist_kvno) {
1071 ret = KADM5_BAD_HIST_KEY;
1075 if (ret = check_pw_reuse(handle->context,
1077 kdb.n_key_data, kdb.key_data,
1078 adb.old_key_len, adb.old_keys))
1081 if (ret = add_to_history(handle->context, &adb, &pol, &hist))
1086 if (pol.pw_max_life)
1087 kdb.pw_expiration = now + pol.pw_max_life;
1089 kdb.pw_expiration = 0;
1091 kdb.pw_expiration = 0;
1094 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1097 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1102 if (!hist_added && hist.key_data)
1103 free_history_entry(handle->context, &hist);
1104 kdb_free_entry(handle, &kdb, &adb);
1105 kdb_free_entry(handle, &kdb_save, NULL);
1106 krb5_dbe_free_contents(handle->context, &kdb);
1108 if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1116 kadm5_randkey_principal(void *server_handle,
1117 krb5_principal principal,
1118 krb5_keyblock **keyblocks,
1122 osa_princ_ent_rec adb;
1124 kadm5_policy_ent_rec pol;
1125 krb5_key_data *key_data;
1126 krb5_keyblock *keyblock;
1127 int ret, last_pwd, have_pol = 0;
1128 kadm5_server_handle_t handle = server_handle;
1133 CHECK_HANDLE(server_handle);
1135 if (principal == NULL)
1137 if (hist_princ && /* this will be NULL when initializing the databse */
1138 ((krb5_principal_compare(handle->context,
1139 principal, hist_princ)) == TRUE))
1140 return KADM5_PROTECT_PRINCIPAL;
1142 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1145 if (ret = krb5_dbe_crk(handle->context, &master_encblock,
1146 handle->params.keysalts,
1147 handle->params.num_keysalts,
1151 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1153 if (ret = krb5_timeofday(handle->context, &now))
1156 if ((adb.aux_attributes & KADM5_POLICY)) {
1157 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1162 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1168 * The spec says this check is overridden if the caller has
1169 * modify privilege. The admin server therefore makes this
1170 * check itself (in chpass_principal_wrapper, misc.c). A
1171 * local caller implicitly has all authorization bits.
1173 if((now - last_pwd) < pol.pw_min_life &&
1174 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1175 ret = KADM5_PASS_TOOSOON;
1180 if(pol.pw_history_num > 1) {
1181 if(adb.admin_history_kvno != hist_kvno) {
1182 ret = KADM5_BAD_HIST_KEY;
1186 if (ret = check_pw_reuse(handle->context,
1188 kdb.n_key_data, kdb.key_data,
1189 adb.old_key_len, adb.old_keys))
1192 if (pol.pw_max_life)
1193 kdb.pw_expiration = now + pol.pw_max_life;
1195 kdb.pw_expiration = 0;
1197 kdb.pw_expiration = 0;
1200 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1204 if (handle->api_version == KADM5_API_VERSION_1) {
1205 /* Version 1 clients will expect to see a DES_CRC enctype. */
1206 if (ret = krb5_dbe_find_enctype(handle->context, &kdb,
1207 ENCTYPE_DES_CBC_CRC,
1211 if (ret = decrypt_key_data(handle->context, 1, key_data,
1215 ret = decrypt_key_data(handle->context,
1216 kdb.n_key_data, kdb.key_data,
1223 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1228 kdb_free_entry(handle, &kdb, &adb);
1230 kadm5_free_policy_ent(handle->lhandle, &pol);
1236 * Allocate an array of n_key_data krb5_keyblocks, fill in each
1237 * element with the results of decrypting the nth key in key_data with
1238 * master_encblock, and if n_keys is not NULL fill it in with the
1239 * number of keys decrypted.
1241 static int decrypt_key_data(krb5_context context,
1242 int n_key_data, krb5_key_data *key_data,
1243 krb5_keyblock **keyblocks, int *n_keys)
1245 krb5_keyblock *keys;
1248 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1251 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1253 for (i = 0; i < n_key_data; i++) {
1254 if (ret = krb5_dbekd_decrypt_key_data(context,
1259 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1267 *n_keys = n_key_data;
1273 * Function: kadm5_decrypt_key
1275 * Purpose: Retrieves and decrypts a principal key.
1279 * server_handle (r) kadm5 handle
1280 * entry (r) principal retrieved with kadm5_get_principal
1281 * ktype (r) enctype to search for, or -1 to ignore
1282 * stype (r) salt type to search for, or -1 to ignore
1283 * kvno (r) kvno to search for, -1 for max, 0 for max
1284 * only if it also matches ktype and stype
1285 * keyblock (w) keyblock to fill in
1286 * keysalt (w) keysalt to fill in, or NULL
1287 * kvnop (w) kvno to fill in, or NULL
1289 * Effects: Searches the key_data array of entry, which must have been
1290 * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1291 * find a key with a specified enctype, salt type, and kvno in a
1292 * principal entry. If not found, return ENOENT. Otherwise, decrypt
1293 * it with the master key, and return the key in keyblock, the salt
1294 * in salttype, and the key version number in kvno.
1296 * If ktype or stype is -1, it is ignored for the search. If kvno is
1297 * -1, ktype and stype are ignored and the key with the max kvno is
1298 * returned. If kvno is 0, only the key with the max kvno is returned
1299 * and only if it matches the ktype and stype; otherwise, ENOENT is
1302 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1303 kadm5_principal_ent_t entry, krb5_int32
1304 ktype, krb5_int32 stype, krb5_int32
1305 kvno, krb5_keyblock *keyblock,
1306 krb5_keysalt *keysalt, int *kvnop)
1308 kadm5_server_handle_t handle = server_handle;
1309 krb5_db_entry dbent;
1310 krb5_key_data *key_data;
1313 CHECK_HANDLE(server_handle);
1315 if (entry->n_key_data == 0 || entry->key_data == NULL)
1318 /* find_enctype only uses these two fields */
1319 dbent.n_key_data = entry->n_key_data;
1320 dbent.key_data = entry->key_data;
1321 if (ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1322 stype, kvno, &key_data))
1325 if (ret = krb5_dbekd_decrypt_key_data(handle->context,
1326 &master_encblock, key_data,
1331 *kvnop = key_data->key_data_kvno;