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;
89 /* This is in lib/kdb/kdb_cpw.c, but is static */
90 static void cleanup_key_data(context, count, data)
97 for (i = 0; i < count; i++)
98 for (j = 0; j < data[i].key_data_ver; j++)
99 if (data[i].key_data_length[j])
100 free(data[i].key_data_contents[j]);
105 kadm5_create_principal(void *server_handle,
106 kadm5_principal_ent_t entry, long mask,
110 osa_princ_ent_rec adb;
111 kadm5_policy_ent_rec polent;
113 krb5_tl_data *tl_data_orig, *tl_data_tail;
115 kadm5_server_handle_t handle = server_handle;
117 CHECK_HANDLE(server_handle);
120 * Argument sanity checking, and opening up the DB
122 if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
123 (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
124 (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
125 (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
126 (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
127 (mask & KADM5_FAIL_AUTH_COUNT))
128 return KADM5_BAD_MASK;
129 if((mask & ~ALL_PRINC_MASK))
130 return KADM5_BAD_MASK;
131 if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
135 * Check to see if the principal exists
137 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
140 case KADM5_UNK_PRINC:
143 kdb_free_entry(handle, &kdb, &adb);
149 memset(&kdb, 0, sizeof(krb5_db_entry));
150 memset(&adb, 0, sizeof(osa_princ_ent_rec));
153 * If a policy was specified, load it.
154 * If we can not find the one specified return an error
156 if ((mask & KADM5_POLICY)) {
157 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
158 &polent)) != KADM5_OK) {
160 return KADM5_BAD_POLICY;
165 if (ret = passwd_check(handle, password, (mask & KADM5_POLICY),
166 &polent, entry->principal)) {
167 if (mask & KADM5_POLICY)
168 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
172 * Start populating the various DB fields, using the
173 * "defaults" for fields that were not specified by the
176 if (ret = krb5_timeofday(handle->context, &now)) {
177 if (mask & KADM5_POLICY)
178 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
182 kdb.magic = KRB5_KDB_MAGIC_NUMBER;
183 kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
185 if ((mask & KADM5_ATTRIBUTES))
186 kdb.attributes = entry->attributes;
188 kdb.attributes = handle->params.flags;
190 if ((mask & KADM5_MAX_LIFE))
191 kdb.max_life = entry->max_life;
193 kdb.max_life = handle->params.max_life;
195 if (mask & KADM5_MAX_RLIFE)
196 kdb.max_renewable_life = entry->max_renewable_life;
198 kdb.max_renewable_life = handle->params.max_rlife;
200 if ((mask & KADM5_PRINC_EXPIRE_TIME))
201 kdb.expiration = entry->princ_expire_time;
203 kdb.expiration = handle->params.expiration;
205 kdb.pw_expiration = 0;
206 if ((mask & KADM5_POLICY)) {
207 if(polent.pw_max_life)
208 kdb.pw_expiration = now + polent.pw_max_life;
210 kdb.pw_expiration = 0;
212 if ((mask & KADM5_PW_EXPIRATION))
213 kdb.pw_expiration = entry->pw_expiration;
215 kdb.last_success = 0;
217 kdb.fail_auth_count = 0;
219 /* this is kind of gross, but in order to free the tl data, I need
220 to free the entire kdb entry, and that will try to free the
223 if (ret = krb5_copy_principal(handle->context,
224 entry->principal, &(kdb.princ))) {
225 if (mask & KADM5_POLICY)
226 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
230 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) {
231 krb5_dbe_free_contents(handle->context, &kdb);
232 if (mask & KADM5_POLICY)
233 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
237 /* initialize the keys */
239 if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
240 handle->params.keysalts,
241 handle->params.num_keysalts,
243 (mask & KADM5_KVNO)?entry->kvno:1, &kdb)) {
244 krb5_dbe_free_contents(handle->context, &kdb);
245 if (mask & KADM5_POLICY)
246 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
250 /* populate the admin-server-specific fields. In the OV server,
251 this used to be in a separate database. Since there's already
252 marshalling code for the admin fields, to keep things simple,
253 I'm going to keep it, and make all the admin stuff occupy a
254 single tl_data record, */
256 adb.admin_history_kvno = hist_kvno;
257 if ((mask & KADM5_POLICY)) {
258 adb.aux_attributes = KADM5_POLICY;
260 /* this does *not* need to be strdup'ed, because adb is xdr */
261 /* encoded in osa_adb_create_princ, and not ever freed */
263 adb.policy = entry->policy;
266 /* increment the policy ref count, if any */
268 if ((mask & KADM5_POLICY)) {
269 polent.policy_refcnt++;
270 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
273 krb5_dbe_free_contents(handle->context, &kdb);
274 if (mask & KADM5_POLICY)
275 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
280 if (mask & KADM5_TL_DATA) {
281 /* splice entry->tl_data onto the front of kdb.tl_data */
282 tl_data_orig = kdb.tl_data;
283 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
284 tl_data_tail = tl_data_tail->tl_data_next)
286 tl_data_tail->tl_data_next = kdb.tl_data;
287 kdb.tl_data = entry->tl_data;
290 /* store the new db entry */
291 ret = kdb_put_entry(handle, &kdb, &adb);
293 if (mask & KADM5_TL_DATA) {
294 /* remove entry->tl_data from the front of kdb.tl_data */
295 tl_data_tail->tl_data_next = NULL;
296 kdb.tl_data = tl_data_orig;
299 krb5_dbe_free_contents(handle->context, &kdb);
302 if ((mask & KADM5_POLICY)) {
303 /* decrement the policy ref count */
305 polent.policy_refcnt--;
307 * if this fails, there's nothing we can do anyway. the
308 * policy refcount wil be too high.
310 (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
314 if (mask & KADM5_POLICY)
315 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
319 if (mask & KADM5_POLICY)
320 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
327 kadm5_delete_principal(void *server_handle, krb5_principal principal)
330 kadm5_policy_ent_rec polent;
332 osa_princ_ent_rec adb;
333 kadm5_server_handle_t handle = server_handle;
335 CHECK_HANDLE(server_handle);
337 if (principal == NULL)
340 if (ret = kdb_get_entry(handle, principal, &kdb, &adb))
343 if ((adb.aux_attributes & KADM5_POLICY)) {
344 if ((ret = kadm5_get_policy(handle->lhandle,
345 adb.policy, &polent))
347 polent.policy_refcnt--;
348 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
351 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
352 kdb_free_entry(handle, &kdb, &adb);
356 if (ret = kadm5_free_policy_ent(handle->lhandle, &polent)) {
357 kdb_free_entry(handle, &kdb, &adb);
362 ret = kdb_delete_entry(handle, principal);
364 kdb_free_entry(handle, &kdb, &adb);
370 kadm5_modify_principal(void *server_handle,
371 kadm5_principal_ent_t entry, long mask)
374 kadm5_policy_ent_rec npol, opol;
375 int have_npol = 0, have_opol = 0;
377 krb5_tl_data *tl_data_orig, *tl_data_tail;
378 osa_princ_ent_rec adb;
379 kadm5_server_handle_t handle = server_handle;
381 CHECK_HANDLE(server_handle);
383 if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
384 (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
385 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
386 (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
387 (mask & KADM5_LAST_FAILED))
388 return KADM5_BAD_MASK;
389 if((mask & ~ALL_PRINC_MASK))
390 return KADM5_BAD_MASK;
391 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
392 return KADM5_BAD_MASK;
393 if(entry == (kadm5_principal_ent_t) NULL)
395 if (mask & KADM5_TL_DATA) {
396 tl_data_orig = entry->tl_data;
397 while (tl_data_orig) {
398 if (tl_data_orig->tl_data_type < 256)
399 return KADM5_BAD_TL_TYPE;
400 tl_data_orig = tl_data_orig->tl_data_next;
404 if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb))
408 * This is pretty much the same as create ...
411 if ((mask & KADM5_POLICY)) {
412 /* get the new policy */
413 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
417 ret = KADM5_BAD_POLICY;
419 case KADM5_UNK_POLICY:
420 case KADM5_BAD_POLICY:
421 ret = KADM5_UNK_POLICY;
428 /* if we already have a policy, get it to decrement the refcnt */
429 if(adb.aux_attributes & KADM5_POLICY) {
430 /* ... but not if the old and new are the same */
431 if(strcmp(adb.policy, entry->policy)) {
432 ret = kadm5_get_policy(handle->lhandle,
436 case KADM5_BAD_POLICY:
437 case KADM5_UNK_POLICY:
441 opol.policy_refcnt--;
447 npol.policy_refcnt++;
449 } else npol.policy_refcnt++;
451 /* set us up to use the new policy */
452 adb.aux_attributes |= KADM5_POLICY;
455 adb.policy = strdup(entry->policy);
457 /* set pw_max_life based on new policy */
458 if (npol.pw_max_life) {
459 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
460 &(kdb.pw_expiration)))
462 kdb.pw_expiration += npol.pw_max_life;
464 kdb.pw_expiration = 0;
468 if ((mask & KADM5_POLICY_CLR) &&
469 (adb.aux_attributes & KADM5_POLICY)) {
470 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
473 case KADM5_BAD_POLICY:
474 case KADM5_UNK_POLICY:
483 adb.aux_attributes &= ~KADM5_POLICY;
484 kdb.pw_expiration = 0;
485 opol.policy_refcnt--;
493 if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
496 kadm5_modify_policy_internal(handle->lhandle, &opol,
497 KADM5_REF_COUNT))) ||
500 kadm5_modify_policy_internal(handle->lhandle, &npol,
504 if ((mask & KADM5_ATTRIBUTES))
505 kdb.attributes = entry->attributes;
506 if ((mask & KADM5_MAX_LIFE))
507 kdb.max_life = entry->max_life;
508 if ((mask & KADM5_PRINC_EXPIRE_TIME))
509 kdb.expiration = entry->princ_expire_time;
510 if (mask & KADM5_PW_EXPIRATION)
511 kdb.pw_expiration = entry->pw_expiration;
512 if (mask & KADM5_MAX_RLIFE)
513 kdb.max_renewable_life = entry->max_renewable_life;
514 if (mask & KADM5_FAIL_AUTH_COUNT)
515 kdb.fail_auth_count = entry->fail_auth_count;
517 if((mask & KADM5_KVNO)) {
518 for (i = 0; i < kdb.n_key_data; i++)
519 kdb.key_data[i].key_data_kvno = entry->kvno;
522 if (mask & KADM5_TL_DATA) {
523 krb5_tl_data *tl, *tl2;
525 * Replace kdb.tl_data with what was passed in. The
526 * KRB5_TL_KADM_DATA will be re-added (based on adb) by
527 * kdb_put_entry, below.
529 * Note that we have to duplicate the passed in tl_data
530 * before adding it to kdb. The reason is that kdb_put_entry
531 * will add its own tl_data entries that we will need to
532 * free, but we cannot free the caller's tl_data (an
533 * alternative would be to scan the tl_data after put_entry
534 * and only free those entries that were not passed in).
536 while (kdb.tl_data) {
537 tl = kdb.tl_data->tl_data_next;
538 free(kdb.tl_data->tl_data_contents);
543 kdb.n_tl_data = entry->n_tl_data;
545 tl2 = entry->tl_data;
547 tl = dup_tl_data(tl2);
548 tl->tl_data_next = kdb.tl_data;
550 tl2 = tl2->tl_data_next;
554 ret = kdb_put_entry(handle, &kdb, &adb);
560 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
561 ret = ret ? ret : ret2;
564 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
565 ret = ret ? ret : ret2;
567 kdb_free_entry(handle, &kdb, &adb);
572 kadm5_rename_principal(void *server_handle,
573 krb5_principal source, krb5_principal target)
576 osa_princ_ent_rec adb;
578 kadm5_server_handle_t handle = server_handle;
580 CHECK_HANDLE(server_handle);
582 if (source == NULL || target == NULL)
585 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
586 kdb_free_entry(handle, &kdb, &adb);
590 if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
593 /* this is kinda gross, but unavoidable */
595 for (i=0; i<kdb.n_key_data; i++) {
596 if ((kdb.key_data[i].key_data_ver == 1) ||
597 (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
598 ret = KADM5_NO_RENAME_SALT;
603 krb5_free_principal(handle->context, kdb.princ);
604 if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) {
605 kdb.princ = NULL; /* so freeing the dbe doesn't lose */
609 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
612 ret = kdb_delete_entry(handle, source);
615 kdb_free_entry(handle, &kdb, &adb);
620 kadm5_get_principal(void *server_handle, krb5_principal principal,
621 kadm5_principal_ent_t entry,
625 osa_princ_ent_rec adb;
626 osa_adb_ret_t ret = 0;
629 kadm5_server_handle_t handle = server_handle;
630 kadm5_principal_ent_rec entry_local, *entry_orig;
632 CHECK_HANDLE(server_handle);
635 * In version 1, all the defined fields are always returned.
636 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
637 * filled with allocated memory.
639 if (handle->api_version == KADM5_API_VERSION_1) {
640 mask = KADM5_PRINCIPAL_NORMAL_MASK;
642 entry = &entry_local;
647 memset((char *) entry, 0, sizeof(*entry));
649 if (principal == NULL)
652 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
655 if ((mask & KADM5_POLICY) &&
656 adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
657 if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
661 strcpy(entry->policy, adb.policy);
664 if (mask & KADM5_AUX_ATTRIBUTES)
665 entry->aux_attributes = adb.aux_attributes;
667 if ((mask & KADM5_PRINCIPAL) &&
668 (ret = krb5_copy_principal(handle->context, principal,
669 &entry->principal))) {
673 if (mask & KADM5_PRINC_EXPIRE_TIME)
674 entry->princ_expire_time = kdb.expiration;
676 if ((mask & KADM5_LAST_PWD_CHANGE) &&
677 (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
678 &(entry->last_pwd_change)))) {
682 if (mask & KADM5_PW_EXPIRATION)
683 entry->pw_expiration = kdb.pw_expiration;
684 if (mask & KADM5_MAX_LIFE)
685 entry->max_life = kdb.max_life;
687 /* this is a little non-sensical because the function returns two */
688 /* values that must be checked separately against the mask */
689 if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
690 if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
692 &(entry->mod_name))) {
695 if (! (mask & KADM5_MOD_TIME))
697 if (! (mask & KADM5_MOD_NAME)) {
698 krb5_free_principal(handle->context, entry->principal);
699 entry->principal = NULL;
703 if (mask & KADM5_ATTRIBUTES)
704 entry->attributes = kdb.attributes;
706 if (mask & KADM5_KVNO)
707 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
708 if (kdb.key_data[i].key_data_kvno > entry->kvno)
709 entry->kvno = kdb.key_data[i].key_data_kvno;
711 if (handle->api_version == KADM5_API_VERSION_2)
714 /* XXX I'll be damned if I know how to deal with this one --marc */
719 * The new fields that only exist in version 2 start here
721 if (handle->api_version == KADM5_API_VERSION_2) {
722 if (mask & KADM5_MAX_RLIFE)
723 entry->max_renewable_life = kdb.max_renewable_life;
724 if (mask & KADM5_LAST_SUCCESS)
725 entry->last_success = kdb.last_success;
726 if (mask & KADM5_LAST_FAILED)
727 entry->last_failed = kdb.last_failed;
728 if (mask & KADM5_FAIL_AUTH_COUNT)
729 entry->fail_auth_count = kdb.fail_auth_count;
730 if (mask & KADM5_TL_DATA) {
731 krb5_tl_data td, *tl, *tl2;
733 entry->tl_data = NULL;
737 if (tl->tl_data_type > 255) {
738 if ((tl2 = dup_tl_data(tl)) == NULL) {
742 tl2->tl_data_next = entry->tl_data;
743 entry->tl_data = tl2;
747 tl = tl->tl_data_next;
750 if (mask & KADM5_KEY_DATA) {
751 entry->n_key_data = kdb.n_key_data;
752 if(entry->n_key_data) {
753 entry->key_data = (krb5_key_data *)
754 malloc(entry->n_key_data*sizeof(krb5_key_data));
755 if (entry->key_data == NULL) {
760 entry->key_data = NULL;
762 for (i = 0; i < entry->n_key_data; i++)
763 if (ret = krb5_copy_key_data_contents(handle->context,
765 &entry->key_data[i]))
771 * If KADM5_API_VERSION_1, we return an allocated structure, and
772 * we need to convert the new structure back into the format the
773 * caller is expecting.
775 if (handle->api_version == KADM5_API_VERSION_1) {
776 kadm5_principal_ent_t_v1 newv1;
778 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
784 newv1->principal = entry->principal;
785 newv1->princ_expire_time = entry->princ_expire_time;
786 newv1->last_pwd_change = entry->last_pwd_change;
787 newv1->pw_expiration = entry->pw_expiration;
788 newv1->max_life = entry->max_life;
789 newv1->mod_name = entry->mod_name;
790 newv1->mod_date = entry->mod_date;
791 newv1->attributes = entry->attributes;
792 newv1->kvno = entry->kvno;
793 newv1->mkvno = entry->mkvno;
794 newv1->policy = entry->policy;
795 newv1->aux_attributes = entry->aux_attributes;
797 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
803 if (ret && entry->principal)
804 krb5_free_principal(handle->context, entry->principal);
805 kdb_free_entry(handle, &kdb, &adb);
811 * Function: check_pw_reuse
813 * Purpose: Check if a key appears in a list of keys, in order to
814 * enforce password history.
818 * context (r) the krb5 context
819 * histkey_encblock (r) the encblock that hist_key_data is
821 * n_new_key_data (r) length of new_key_data
822 * new_key_data (r) keys to check against
823 * pw_hist_data, encrypted in histkey_encblock
824 * n_pw_hist_data (r) length of pw_hist_data
825 * pw_hist_data (r) passwords to check new_key_data against
828 * For each new_key in new_key_data:
829 * decrypt new_key with the master_encblock
830 * for each password in pw_hist_data:
831 * for each hist_key in password:
832 * decrypt hist_key with histkey_encblock
833 * compare the new_key and hist_key
835 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
836 * new_key_data is the same as a key in pw_hist_data, or 0.
839 check_pw_reuse(krb5_context context,
840 krb5_encrypt_block *histkey_encblock,
841 int n_new_key_data, krb5_key_data *new_key_data,
842 int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
845 krb5_keyblock newkey, histkey;
848 for (x = 0; x < n_new_key_data; x++) {
849 if (ret = krb5_dbekd_decrypt_key_data(context,
854 for (y = 0; y < n_pw_hist_data; y++) {
855 for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
857 krb5_dbekd_decrypt_key_data(context,
859 &pw_hist_data[y].key_data[z],
863 if ((newkey.length == histkey.length) &&
864 (newkey.enctype == histkey.enctype) &&
865 (memcmp(newkey.contents, histkey.contents,
866 histkey.length) == 0)) {
867 krb5_free_keyblock_contents(context, &histkey);
868 krb5_free_keyblock_contents(context, &newkey);
870 return(KADM5_PASS_REUSE);
872 krb5_free_keyblock_contents(context, &histkey);
875 krb5_free_keyblock_contents(context, &newkey);
882 * Function: create_history_entry
884 * Purpose: Creates a password history entry from an array of
889 * context (r) krb5_context to use
890 * n_key_data (r) number of elements in key_data
891 * key_data (r) keys to add to the history entry
892 * hist (w) history entry to fill in
896 * hist->key_data is allocated to store n_key_data key_datas. Each
897 * element of key_data is decrypted with master_encblock, re-encrypted
898 * in hist_encblock, and added to hist->key_data. hist->n_key_data is
901 int create_history_entry(krb5_context context, int n_key_data,
902 krb5_key_data *key_data, osa_pw_hist_ent *hist)
908 hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
909 if (hist->key_data == NULL)
911 memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
913 for (i = 0; i < n_key_data; i++) {
914 if (ret = krb5_dbekd_decrypt_key_data(context,
919 if (ret = krb5_dbekd_encrypt_key_data(context,
922 key_data[i].key_data_kvno,
925 krb5_free_keyblock_contents(context, &key);
926 /* krb5_free_keysalt(context, &salt); */
929 hist->n_key_data = n_key_data;
933 int free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
937 for (i = 0; i < hist->n_key_data; i++)
938 krb5_free_key_data_contents(context, &hist->key_data[i]);
939 free(hist->key_data);
943 * Function: add_to_history
945 * Purpose: Adds a password to a principal's password history.
949 * context (r) krb5_context to use
950 * adb (r/w) admin principal entry to add keys to
951 * pol (r) adb's policy
952 * pw (r) keys for the password to add to adb's key history
956 * add_to_history adds a single password to adb's password history.
957 * pw contains n_key_data keys in its key_data, in storage should be
958 * allocated but not freed by the caller (XXX blech!).
960 * This function maintains adb->old_keys as a circular queue. It
961 * starts empty, and grows each time this function is called until it
962 * is pol->pw_history_num items long. adb->old_key_len holds the
963 * number of allocated entries in the array, and must therefore be [0,
964 * pol->pw_history_num). adb->old_key_next is the index into the
965 * array where the next element should be written, and must be [0,
968 static kadm5_ret_t add_to_history(krb5_context context,
970 kadm5_policy_ent_t pol,
973 osa_pw_hist_ent hist, *histp;
976 /* A history of 1 means just check the current password */
977 if (pol->pw_history_num == 1)
980 /* resize the adb->old_keys array if necessary */
981 if (adb->old_key_len < pol->pw_history_num-1) {
982 if (adb->old_keys == NULL) {
983 adb->old_keys = (osa_pw_hist_ent *)
984 malloc((adb->old_key_len + 1) * sizeof (osa_pw_hist_ent));
986 adb->old_keys = (osa_pw_hist_ent *)
987 realloc(adb->old_keys,
988 (adb->old_key_len + 1) * sizeof (osa_pw_hist_ent));
990 if (adb->old_keys == NULL)
993 memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent));
997 /* free the old pw history entry if it contains data */
998 histp = &adb->old_keys[adb->old_key_next];
999 for (i = 0; i < histp->n_key_data; i++)
1000 krb5_free_key_data_contents(context, &histp->key_data[i]);
1002 /* store the new entry */
1003 adb->old_keys[adb->old_key_next] = *pw;
1005 /* update the next pointer */
1006 if (++adb->old_key_next == pol->pw_history_num-1)
1007 adb->old_key_next = 0;
1013 kadm5_chpass_principal(void *server_handle,
1014 krb5_principal principal, char *password)
1017 kadm5_policy_ent_rec pol;
1018 osa_princ_ent_rec adb;
1019 krb5_db_entry kdb, kdb_save;
1020 int ret, ret2, last_pwd, i, hist_added;
1022 kadm5_server_handle_t handle = server_handle;
1023 osa_pw_hist_ent hist;
1025 CHECK_HANDLE(server_handle);
1028 memset(&hist, 0, sizeof(hist));
1030 if (principal == NULL || password == NULL)
1032 if ((krb5_principal_compare(handle->context,
1033 principal, hist_princ)) == TRUE)
1034 return KADM5_PROTECT_PRINCIPAL;
1036 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1039 /* we are going to need the current keys after the new keys are set */
1040 if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1041 kdb_free_entry(handle, &kdb, &adb);
1045 if ((adb.aux_attributes & KADM5_POLICY)) {
1046 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1051 if ((ret = passwd_check(handle, password, adb.aux_attributes &
1052 KADM5_POLICY, &pol, principal)))
1055 if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
1056 handle->params.keysalts,
1057 handle->params.num_keysalts,
1058 password, 0 /* increment kvno */, &kdb))
1061 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1063 if (ret = krb5_timeofday(handle->context, &now))
1066 if ((adb.aux_attributes & KADM5_POLICY)) {
1067 /* the policy was loaded before */
1069 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1075 * The spec says this check is overridden if the caller has
1076 * modify privilege. The admin server therefore makes this
1077 * check itself (in chpass_principal_wrapper, misc.c). A
1078 * local caller implicitly has all authorization bits.
1080 if ((now - last_pwd) < pol.pw_min_life &&
1081 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1082 ret = KADM5_PASS_TOOSOON;
1087 if (ret = create_history_entry(handle->context,
1088 kdb_save.n_key_data,
1089 kdb_save.key_data, &hist))
1092 if (ret = check_pw_reuse(handle->context,
1094 kdb.n_key_data, kdb.key_data,
1098 if (pol.pw_history_num > 1) {
1099 if (adb.admin_history_kvno != hist_kvno) {
1100 ret = KADM5_BAD_HIST_KEY;
1104 if (ret = check_pw_reuse(handle->context,
1106 kdb.n_key_data, kdb.key_data,
1107 adb.old_key_len, adb.old_keys))
1110 if (ret = add_to_history(handle->context, &adb, &pol, &hist))
1115 if (pol.pw_max_life)
1116 kdb.pw_expiration = now + pol.pw_max_life;
1118 kdb.pw_expiration = 0;
1120 kdb.pw_expiration = 0;
1123 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1126 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1131 if (!hist_added && hist.key_data)
1132 free_history_entry(handle->context, &hist);
1133 kdb_free_entry(handle, &kdb, &adb);
1134 kdb_free_entry(handle, &kdb_save, NULL);
1135 krb5_dbe_free_contents(handle->context, &kdb);
1137 if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1145 kadm5_randkey_principal(void *server_handle,
1146 krb5_principal principal,
1147 krb5_keyblock **keyblocks,
1151 osa_princ_ent_rec adb;
1153 kadm5_policy_ent_rec pol;
1154 krb5_key_data *key_data;
1155 krb5_keyblock *keyblock;
1156 int ret, last_pwd, have_pol = 0;
1157 kadm5_server_handle_t handle = server_handle;
1162 CHECK_HANDLE(server_handle);
1164 if (principal == NULL)
1166 if (hist_princ && /* this will be NULL when initializing the databse */
1167 ((krb5_principal_compare(handle->context,
1168 principal, hist_princ)) == TRUE))
1169 return KADM5_PROTECT_PRINCIPAL;
1171 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1174 if (ret = krb5_dbe_crk(handle->context, &master_encblock,
1175 handle->params.keysalts,
1176 handle->params.num_keysalts,
1180 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1182 if (ret = krb5_timeofday(handle->context, &now))
1185 if ((adb.aux_attributes & KADM5_POLICY)) {
1186 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1191 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1197 * The spec says this check is overridden if the caller has
1198 * modify privilege. The admin server therefore makes this
1199 * check itself (in chpass_principal_wrapper, misc.c). A
1200 * local caller implicitly has all authorization bits.
1202 if((now - last_pwd) < pol.pw_min_life &&
1203 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1204 ret = KADM5_PASS_TOOSOON;
1209 if(pol.pw_history_num > 1) {
1210 if(adb.admin_history_kvno != hist_kvno) {
1211 ret = KADM5_BAD_HIST_KEY;
1215 if (ret = check_pw_reuse(handle->context,
1217 kdb.n_key_data, kdb.key_data,
1218 adb.old_key_len, adb.old_keys))
1221 if (pol.pw_max_life)
1222 kdb.pw_expiration = now + pol.pw_max_life;
1224 kdb.pw_expiration = 0;
1226 kdb.pw_expiration = 0;
1229 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1233 if (handle->api_version == KADM5_API_VERSION_1) {
1234 /* Version 1 clients will expect to see a DES_CRC enctype. */
1235 if (ret = krb5_dbe_find_enctype(handle->context, &kdb,
1236 ENCTYPE_DES_CBC_CRC,
1240 if (ret = decrypt_key_data(handle->context, 1, key_data,
1244 ret = decrypt_key_data(handle->context,
1245 kdb.n_key_data, kdb.key_data,
1252 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1257 kdb_free_entry(handle, &kdb, &adb);
1259 kadm5_free_policy_ent(handle->lhandle, &pol);
1265 * kadm5_setv4key_principal:
1267 * Set only ONE key of the principal, removing all others. This key
1268 * must have the DES_CBC_CRC enctype and is entered as having the
1269 * krb4 salttype. This is to enable things like kadmind4 to work.
1272 kadm5_setv4key_principal(void *server_handle,
1273 krb5_principal principal,
1274 krb5_keyblock *keyblock)
1277 osa_princ_ent_rec adb;
1279 kadm5_policy_ent_rec pol;
1280 krb5_key_data *key_data;
1281 krb5_keysalt keysalt;
1282 int i, kvno, ret, last_pwd, have_pol = 0;
1284 kadm5_server_handle_t handle = server_handle;
1286 CHECK_HANDLE(server_handle);
1288 if (principal == NULL || keyblock == NULL)
1290 if (hist_princ && /* this will be NULL when initializing the databse */
1291 ((krb5_principal_compare(handle->context,
1292 principal, hist_princ)) == TRUE))
1293 return KADM5_PROTECT_PRINCIPAL;
1295 if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1296 return KADM5_SETV4KEY_INVAL_ENCTYPE;
1298 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1301 for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1302 if (kdb.key_data[i].key_data_kvno > kvno)
1303 kvno = kdb.key_data[i].key_data_kvno;
1305 if (kdb.key_data != NULL)
1306 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1308 kdb.key_data = (krb5_key_data*)malloc(sizeof(krb5_key_data));
1309 if (kdb.key_data == NULL)
1311 memset(kdb.key_data, 0, sizeof(krb5_key_data));
1313 keysalt.type = KRB5_KDB_SALTTYPE_V4;
1314 /* XXX data.magic? */
1315 keysalt.data.length = 0;
1316 keysalt.data.data = NULL;
1318 if (ret = krb5_dbekd_encrypt_key_data(handle->context,
1326 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1328 if (ret = krb5_timeofday(handle->context, &now))
1331 if ((adb.aux_attributes & KADM5_POLICY)) {
1332 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1339 * The spec says this check is overridden if the caller has
1340 * modify privilege. The admin server therefore makes this
1341 * check itself (in chpass_principal_wrapper, misc.c). A
1342 * local caller implicitly has all authorization bits.
1344 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1347 if((now - last_pwd) < pol.pw_min_life &&
1348 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1349 ret = KADM5_PASS_TOOSOON;
1355 * Should we be checking/updating pw history here?
1357 if(pol.pw_history_num > 1) {
1358 if(adb.admin_history_kvno != hist_kvno) {
1359 ret = KADM5_BAD_HIST_KEY;
1363 if (ret = check_pw_reuse(handle->context,
1365 kdb.n_key_data, kdb.key_data,
1366 adb.old_key_len, adb.old_keys))
1371 if (pol.pw_max_life)
1372 kdb.pw_expiration = now + pol.pw_max_life;
1374 kdb.pw_expiration = 0;
1376 kdb.pw_expiration = 0;
1379 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1382 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1387 kdb_free_entry(handle, &kdb, &adb);
1389 kadm5_free_policy_ent(handle->lhandle, &pol);
1395 kadm5_setkey_principal(void *server_handle,
1396 krb5_principal principal,
1397 krb5_keyblock *keyblocks,
1401 osa_princ_ent_rec adb;
1403 kadm5_policy_ent_rec pol;
1404 krb5_key_data *key_data;
1405 int i, kvno, ret, last_pwd, have_pol = 0;
1407 kadm5_server_handle_t handle = server_handle;
1409 CHECK_HANDLE(server_handle);
1411 if (principal == NULL || keyblocks == NULL)
1413 if (hist_princ && /* this will be NULL when initializing the databse */
1414 ((krb5_principal_compare(handle->context,
1415 principal, hist_princ)) == TRUE))
1416 return KADM5_PROTECT_PRINCIPAL;
1418 for (i = 0, deskeys = 0; i < n_keys; i++) {
1419 if (keyblocks[i].enctype == ENCTYPE_DES_CBC_MD4 ||
1420 keyblocks[i].enctype == ENCTYPE_DES_CBC_MD5 ||
1421 keyblocks[i].enctype == ENCTYPE_DES_CBC_RAW ||
1422 keyblocks[i].enctype == ENCTYPE_DES_CBC_CRC)
1425 return KADM5_SETKEY_DUP_ENCTYPES;
1428 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1431 for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1432 if (kdb.key_data[i].key_data_kvno > kvno)
1433 kvno = kdb.key_data[i].key_data_kvno;
1435 if (kdb.key_data != NULL)
1436 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1438 kdb.key_data = (krb5_key_data*)malloc(n_keys*sizeof(krb5_key_data));
1439 if (kdb.key_data == NULL)
1441 memset(kdb.key_data, 0, n_keys*sizeof(krb5_key_data));
1442 kdb.n_key_data = n_keys;
1444 for (i = 0; i < n_keys; i++) {
1445 if (ret = krb5_dbekd_encrypt_key_data(handle->context,
1447 &keyblocks[i], NULL,
1453 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1455 if (ret = krb5_timeofday(handle->context, &now))
1458 if ((adb.aux_attributes & KADM5_POLICY)) {
1459 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1466 * The spec says this check is overridden if the caller has
1467 * modify privilege. The admin server therefore makes this
1468 * check itself (in chpass_principal_wrapper, misc.c). A
1469 * local caller implicitly has all authorization bits.
1471 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1474 if((now - last_pwd) < pol.pw_min_life &&
1475 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1476 ret = KADM5_PASS_TOOSOON;
1482 * Should we be checking/updating pw history here?
1484 if(pol.pw_history_num > 1) {
1485 if(adb.admin_history_kvno != hist_kvno) {
1486 ret = KADM5_BAD_HIST_KEY;
1490 if (ret = check_pw_reuse(handle->context,
1492 kdb.n_key_data, kdb.key_data,
1493 adb.old_key_len, adb.old_keys))
1498 if (pol.pw_max_life)
1499 kdb.pw_expiration = now + pol.pw_max_life;
1501 kdb.pw_expiration = 0;
1503 kdb.pw_expiration = 0;
1506 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1509 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1514 kdb_free_entry(handle, &kdb, &adb);
1516 kadm5_free_policy_ent(handle->lhandle, &pol);
1522 * Allocate an array of n_key_data krb5_keyblocks, fill in each
1523 * element with the results of decrypting the nth key in key_data with
1524 * master_encblock, and if n_keys is not NULL fill it in with the
1525 * number of keys decrypted.
1527 static int decrypt_key_data(krb5_context context,
1528 int n_key_data, krb5_key_data *key_data,
1529 krb5_keyblock **keyblocks, int *n_keys)
1531 krb5_keyblock *keys;
1534 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1537 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1539 for (i = 0; i < n_key_data; i++) {
1540 if (ret = krb5_dbekd_decrypt_key_data(context,
1545 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1553 *n_keys = n_key_data;
1559 * Function: kadm5_decrypt_key
1561 * Purpose: Retrieves and decrypts a principal key.
1565 * server_handle (r) kadm5 handle
1566 * entry (r) principal retrieved with kadm5_get_principal
1567 * ktype (r) enctype to search for, or -1 to ignore
1568 * stype (r) salt type to search for, or -1 to ignore
1569 * kvno (r) kvno to search for, -1 for max, 0 for max
1570 * only if it also matches ktype and stype
1571 * keyblock (w) keyblock to fill in
1572 * keysalt (w) keysalt to fill in, or NULL
1573 * kvnop (w) kvno to fill in, or NULL
1575 * Effects: Searches the key_data array of entry, which must have been
1576 * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1577 * find a key with a specified enctype, salt type, and kvno in a
1578 * principal entry. If not found, return ENOENT. Otherwise, decrypt
1579 * it with the master key, and return the key in keyblock, the salt
1580 * in salttype, and the key version number in kvno.
1582 * If ktype or stype is -1, it is ignored for the search. If kvno is
1583 * -1, ktype and stype are ignored and the key with the max kvno is
1584 * returned. If kvno is 0, only the key with the max kvno is returned
1585 * and only if it matches the ktype and stype; otherwise, ENOENT is
1588 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1589 kadm5_principal_ent_t entry, krb5_int32
1590 ktype, krb5_int32 stype, krb5_int32
1591 kvno, krb5_keyblock *keyblock,
1592 krb5_keysalt *keysalt, int *kvnop)
1594 kadm5_server_handle_t handle = server_handle;
1595 krb5_db_entry dbent;
1596 krb5_key_data *key_data;
1599 CHECK_HANDLE(server_handle);
1601 if (entry->n_key_data == 0 || entry->key_data == NULL)
1604 /* find_enctype only uses these two fields */
1605 dbent.n_key_data = entry->n_key_data;
1606 dbent.key_data = entry->key_data;
1607 if (ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1608 stype, kvno, &key_data))
1611 if (ret = krb5_dbekd_decrypt_key_data(handle->context,
1612 &master_encblock, key_data,
1617 *kvnop = key_data->key_data_kvno;