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 int krb5_free_keyblock_contents(context, key)
44 memset(key->contents, 0, key->length);
45 krb5_xfree(key->contents);
49 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
51 krb5_key_data *from, *to;
57 idx = (from->key_data_ver == 1 ? 1 : 2);
59 for (i = 0; i < idx; i++) {
60 if ( from->key_data_length[i] ) {
61 to->key_data_contents[i] = malloc(from->key_data_length[i]);
62 if (to->key_data_contents[i] == NULL) {
63 for (i = 0; i < idx; i++) {
64 if (to->key_data_contents[i]) {
65 memset(to->key_data_contents[i], 0,
66 to->key_data_length[i]);
67 free(to->key_data_contents[i]);
72 memcpy(to->key_data_contents[i], from->key_data_contents[i],
73 from->key_data_length[i]);
79 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
83 n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
86 n->tl_data_contents = malloc(tl->tl_data_length);
87 if (n->tl_data_contents == NULL) {
91 memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
92 n->tl_data_type = tl->tl_data_type;
93 n->tl_data_length = tl->tl_data_length;
94 n->tl_data_next = NULL;
99 kadm5_create_principal(void *server_handle,
100 kadm5_principal_ent_t entry, long mask,
104 osa_princ_ent_rec adb;
105 kadm5_policy_ent_rec polent;
107 krb5_tl_data *tl_data_orig, *tl_data_tail;
109 kadm5_server_handle_t handle = server_handle;
111 CHECK_HANDLE(server_handle);
114 * Argument sanity checking, and opening up the DB
116 if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
117 (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
118 (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
119 (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
120 (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
121 (mask & KADM5_FAIL_AUTH_COUNT))
122 return KADM5_BAD_MASK;
123 if((mask & ~ALL_PRINC_MASK))
124 return KADM5_BAD_MASK;
125 if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
129 * Check to see if the principal exists
131 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
134 case KADM5_UNK_PRINC:
137 kdb_free_entry(handle, &kdb, &adb);
143 memset(&kdb, 0, sizeof(krb5_db_entry));
144 memset(&adb, 0, sizeof(osa_princ_ent_rec));
147 * If a policy was specified, load it.
148 * If we can not find the one specified return an error
150 if ((mask & KADM5_POLICY)) {
151 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
152 &polent)) != KADM5_OK) {
154 return KADM5_BAD_POLICY;
159 if (ret = passwd_check(handle, password, (mask & KADM5_POLICY),
160 &polent, entry->principal)) {
161 if (mask & KADM5_POLICY)
162 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
166 * Start populating the various DB fields, using the
167 * "defaults" for fields that were not specified by the
170 if (ret = krb5_timeofday(handle->context, &now)) {
171 if (mask & KADM5_POLICY)
172 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
176 kdb.magic = KRB5_KDB_MAGIC_NUMBER;
177 kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
179 if ((mask & KADM5_ATTRIBUTES))
180 kdb.attributes = entry->attributes;
182 kdb.attributes = handle->params.flags;
184 if ((mask & KADM5_MAX_LIFE))
185 kdb.max_life = entry->max_life;
187 kdb.max_life = handle->params.max_life;
189 if (mask & KADM5_MAX_RLIFE)
190 kdb.max_renewable_life = entry->max_renewable_life;
192 kdb.max_renewable_life = handle->params.max_rlife;
194 if ((mask & KADM5_PRINC_EXPIRE_TIME))
195 kdb.expiration = entry->princ_expire_time;
197 kdb.expiration = handle->params.expiration;
199 kdb.pw_expiration = 0;
200 if ((mask & KADM5_POLICY)) {
201 if(polent.pw_max_life)
202 kdb.pw_expiration = now + polent.pw_max_life;
204 kdb.pw_expiration = 0;
206 if ((mask & KADM5_PW_EXPIRATION)) {
207 if(!kdb.pw_expiration)
208 kdb.pw_expiration = entry->pw_expiration;
210 if(entry->pw_expiration != 0)
211 kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
212 entry->pw_expiration : kdb.pw_expiration;
216 kdb.last_success = 0;
218 kdb.fail_auth_count = 0;
220 /* this is kind of gross, but in order to free the tl data, I need
221 to free the entire kdb entry, and that will try to free the
224 if (ret = krb5_copy_principal(handle->context,
225 entry->principal, &(kdb.princ))) {
226 if (mask & KADM5_POLICY)
227 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
231 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) {
232 krb5_dbe_free_contents(handle->context, &kdb);
233 if (mask & KADM5_POLICY)
234 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
238 /* initialize the keys */
240 if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
241 handle->params.keysalts,
242 handle->params.num_keysalts,
244 (mask & KADM5_KVNO)?entry->kvno:1, &kdb)) {
245 krb5_dbe_free_contents(handle->context, &kdb);
246 if (mask & KADM5_POLICY)
247 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
251 /* populate the admin-server-specific fields. In the OV server,
252 this used to be in a separate database. Since there's already
253 marshalling code for the admin fields, to keep things simple,
254 I'm going to keep it, and make all the admin stuff occupy a
255 single tl_data record, */
257 adb.admin_history_kvno = hist_kvno;
258 if ((mask & KADM5_POLICY)) {
259 adb.aux_attributes = KADM5_POLICY;
261 /* this does *not* need to be strdup'ed, because adb is xdr */
262 /* encoded in osa_adb_create_princ, and not ever freed */
264 adb.policy = entry->policy;
267 /* increment the policy ref count, if any */
269 if ((mask & KADM5_POLICY)) {
270 polent.policy_refcnt++;
271 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
274 krb5_dbe_free_contents(handle->context, &kdb);
275 if (mask & KADM5_POLICY)
276 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
281 if (mask & KADM5_TL_DATA) {
282 /* splice entry->tl_data onto the front of kdb.tl_data */
283 tl_data_orig = kdb.tl_data;
284 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
285 tl_data_tail = tl_data_tail->tl_data_next)
287 tl_data_tail->tl_data_next = kdb.tl_data;
288 kdb.tl_data = entry->tl_data;
291 /* store the new db entry */
292 ret = kdb_put_entry(handle, &kdb, &adb);
294 if (mask & KADM5_TL_DATA) {
295 /* remove entry->tl_data from the front of kdb.tl_data */
296 tl_data_tail->tl_data_next = NULL;
297 kdb.tl_data = tl_data_orig;
300 krb5_dbe_free_contents(handle->context, &kdb);
303 if ((mask & KADM5_POLICY)) {
304 /* decrement the policy ref count */
306 polent.policy_refcnt--;
308 * if this fails, there's nothing we can do anyway. the
309 * policy refcount wil be too high.
311 (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
315 if (mask & KADM5_POLICY)
316 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
320 if (mask & KADM5_POLICY)
321 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
328 kadm5_delete_principal(void *server_handle, krb5_principal principal)
331 kadm5_policy_ent_rec polent;
333 osa_princ_ent_rec adb;
334 kadm5_server_handle_t handle = server_handle;
336 CHECK_HANDLE(server_handle);
338 if (principal == NULL)
341 if (ret = kdb_get_entry(handle, principal, &kdb, &adb))
344 if ((adb.aux_attributes & KADM5_POLICY)) {
345 if ((ret = kadm5_get_policy(handle->lhandle,
346 adb.policy, &polent))
348 polent.policy_refcnt--;
349 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
352 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
353 kdb_free_entry(handle, &kdb, &adb);
357 if (ret = kadm5_free_policy_ent(handle->lhandle, &polent)) {
358 kdb_free_entry(handle, &kdb, &adb);
363 ret = kdb_delete_entry(handle, principal);
365 kdb_free_entry(handle, &kdb, &adb);
371 kadm5_modify_principal(void *server_handle,
372 kadm5_principal_ent_t entry, long mask)
375 kadm5_policy_ent_rec npol, opol;
376 int have_npol = 0, have_opol = 0;
378 krb5_tl_data *tl_data_orig, *tl_data_tail;
379 osa_princ_ent_rec adb;
380 kadm5_server_handle_t handle = server_handle;
382 CHECK_HANDLE(server_handle);
384 if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
385 (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
386 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
387 (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
388 (mask & KADM5_LAST_FAILED))
389 return KADM5_BAD_MASK;
390 if((mask & ~ALL_PRINC_MASK))
391 return KADM5_BAD_MASK;
392 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
393 return KADM5_BAD_MASK;
394 if(entry == (kadm5_principal_ent_t) NULL)
397 if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb))
401 * This is pretty much the same as create ...
404 if ((mask & KADM5_POLICY)) {
405 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
408 ret = KADM5_BAD_POLICY;
410 case KADM5_UNK_POLICY:
411 case KADM5_BAD_POLICY:
412 ret = KADM5_UNK_POLICY;
417 if(adb.aux_attributes & KADM5_POLICY) {
418 if(strcmp(adb.policy, entry->policy)) {
419 ret = kadm5_get_policy(handle->lhandle,
423 case KADM5_BAD_POLICY:
424 case KADM5_UNK_POLICY:
428 opol.policy_refcnt--;
434 npol.policy_refcnt++;
436 } else npol.policy_refcnt++;
437 adb.aux_attributes |= KADM5_POLICY;
440 adb.policy = strdup(entry->policy);
441 if (npol.pw_max_life) {
443 krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
444 &(kdb.pw_expiration)))
446 kdb.pw_expiration += npol.pw_max_life;
448 kdb.pw_expiration = 0;
454 if ((mask & KADM5_PW_EXPIRATION)) {
455 if(kdb.pw_expiration == 0)
456 kdb.pw_expiration = entry->pw_expiration;
457 else if(entry->pw_expiration != 0)
458 kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
459 entry->pw_expiration : kdb.pw_expiration;
462 if ((mask & KADM5_PW_EXPIRATION) && !(mask & KADM5_POLICY)) {
463 if(kdb.pw_expiration == 0)
464 kdb.pw_expiration = entry->pw_expiration;
465 else if(entry->pw_expiration != 0)
466 kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
467 entry->pw_expiration : kdb.pw_expiration;
470 if ((mask & KADM5_POLICY_CLR)) {
471 if (adb.aux_attributes & KADM5_POLICY) {
472 adb.aux_attributes &= ~KADM5_POLICY;
473 kdb.pw_expiration = 0;
474 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
477 case KADM5_BAD_POLICY:
478 case KADM5_UNK_POLICY:
487 opol.policy_refcnt--;
495 if (((mask & KADM5_POLICY) ||
496 (mask & KADM5_POLICY_CLR)) &&
499 kadm5_modify_policy_internal(handle->lhandle, &opol,
500 KADM5_REF_COUNT))) ||
503 kadm5_modify_policy_internal(handle->lhandle, &npol,
507 if ((mask & KADM5_ATTRIBUTES))
508 kdb.attributes = entry->attributes;
509 if ((mask & KADM5_MAX_LIFE))
510 kdb.max_life = entry->max_life;
511 if ((mask & KADM5_PRINC_EXPIRE_TIME))
512 kdb.expiration = entry->princ_expire_time;
513 /* the pw_expiration logic would go here if it wasn't spread
514 all over the policy code */
515 if (mask & KADM5_MAX_RLIFE)
516 kdb.max_renewable_life = entry->max_renewable_life;
517 if (mask & KADM5_FAIL_AUTH_COUNT)
518 kdb.fail_auth_count = entry->fail_auth_count;
520 if((mask & KADM5_KVNO)) {
521 for (i = 0; i < kdb.n_key_data; i++)
522 kdb.key_data[i].key_data_kvno = entry->kvno;
525 if (mask & KADM5_TL_DATA) {
526 /* splice entry->tl_data onto the front of kdb.tl_data */
527 tl_data_orig = kdb.tl_data;
528 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
529 tl_data_tail = tl_data_tail->tl_data_next)
531 tl_data_tail->tl_data_next = kdb.tl_data;
532 kdb.tl_data = entry->tl_data;
535 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
538 if (mask & KADM5_TL_DATA) {
539 /* remove entry->tl_data from the front of kdb.tl_data */
540 tl_data_tail->tl_data_next = NULL;
541 kdb.tl_data = tl_data_orig;
547 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
548 ret = ret ? ret : ret2;
551 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
552 ret = ret ? ret : ret2;
554 kdb_free_entry(handle, &kdb, &adb);
559 kadm5_rename_principal(void *server_handle,
560 krb5_principal source, krb5_principal target)
563 osa_princ_ent_rec adb;
565 kadm5_server_handle_t handle = server_handle;
567 CHECK_HANDLE(server_handle);
569 if (source == NULL || target == NULL)
572 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
573 kdb_free_entry(handle, &kdb, &adb);
577 if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
580 /* this is kinda gross, but unavoidable */
582 for (i=0; i<kdb.n_key_data; i++) {
583 if ((kdb.key_data[i].key_data_ver == 1) ||
584 (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
585 ret = KADM5_NO_RENAME_SALT;
590 krb5_free_principal(handle->context, kdb.princ);
591 if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) {
592 kdb.princ = NULL; /* so freeing the dbe doesn't lose */
596 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
599 ret = kdb_delete_entry(handle, source);
602 kdb_free_entry(handle, &kdb, &adb);
607 kadm5_get_principal(void *server_handle, krb5_principal principal,
608 kadm5_principal_ent_t entry,
612 osa_princ_ent_rec adb;
613 osa_adb_ret_t ret = 0;
616 kadm5_server_handle_t handle = server_handle;
617 kadm5_principal_ent_rec entry_local, *entry_orig;
619 CHECK_HANDLE(server_handle);
622 * In version 1, all the defined fields are always returned.
623 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
624 * filled with allocated memory.
626 if (handle->api_version == KADM5_API_VERSION_1) {
627 mask = KADM5_PRINCIPAL_NORMAL_MASK;
629 entry = &entry_local;
634 memset((char *) entry, 0, sizeof(*entry));
636 if (principal == NULL)
639 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
642 if ((mask & KADM5_POLICY) &&
643 adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
644 if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
648 strcpy(entry->policy, adb.policy);
651 if (mask & KADM5_AUX_ATTRIBUTES)
652 entry->aux_attributes = adb.aux_attributes;
654 if ((mask & KADM5_PRINCIPAL) &&
655 (ret = krb5_copy_principal(handle->context, principal,
656 &entry->principal))) {
660 if (mask & KADM5_PRINC_EXPIRE_TIME)
661 entry->princ_expire_time = kdb.expiration;
663 if ((mask & KADM5_LAST_PWD_CHANGE) &&
664 (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
665 &(entry->last_pwd_change)))) {
669 if (mask & KADM5_PW_EXPIRATION)
670 entry->pw_expiration = kdb.pw_expiration;
671 if (mask & KADM5_MAX_LIFE)
672 entry->max_life = kdb.max_life;
674 /* this is a little non-sensical because the function returns two */
675 /* values that must be checked separately against the mask */
676 if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
677 if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
679 &(entry->mod_name))) {
682 if (! (mask & KADM5_MOD_TIME))
684 if (! (mask & KADM5_MOD_NAME)) {
685 krb5_free_principal(handle->context, entry->principal);
686 entry->principal = NULL;
690 if (mask & KADM5_ATTRIBUTES)
691 entry->attributes = kdb.attributes;
693 if (mask & KADM5_KVNO)
694 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
695 if (kdb.key_data[i].key_data_kvno > entry->kvno)
696 entry->kvno = kdb.key_data[i].key_data_kvno;
698 if (handle->api_version == KADM5_API_VERSION_2)
701 /* XXX I'll be damned if I know how to deal with this one --marc */
706 * The new fields that only exist in version 2 start here
708 if (handle->api_version == KADM5_API_VERSION_2) {
709 if (mask & KADM5_MAX_RLIFE)
710 entry->max_renewable_life = kdb.max_renewable_life;
711 if (mask & KADM5_LAST_SUCCESS)
712 entry->last_success = kdb.last_success;
713 if (mask & KADM5_LAST_FAILED)
714 entry->last_failed = kdb.last_failed;
715 if (mask & KADM5_FAIL_AUTH_COUNT)
716 entry->fail_auth_count = kdb.fail_auth_count;
717 if (mask & KADM5_TL_DATA) {
718 krb5_tl_data td, *tl, *tl2;
720 entry->n_tl_data = kdb.n_tl_data;
721 entry->tl_data = NULL;
725 if ((tl2 = dup_tl_data(tl)) == NULL) {
729 tl2->tl_data_next = entry->tl_data;
730 entry->tl_data = tl2;
732 tl = tl->tl_data_next;
736 td.tl_data_type = KRB5_TL_KADM5_E_DATA;
737 td.tl_data_length = kdb.e_length;
738 td.tl_data_contents = kdb.e_data;
740 if ((tl = dup_tl_data(&td)) == NULL) {
744 tl->tl_data_next = entry->tl_data;
748 if (mask & KADM5_KEY_DATA) {
749 entry->n_key_data = kdb.n_key_data;
750 entry->key_data = (krb5_key_data *)
751 malloc(entry->n_key_data*sizeof(krb5_key_data));
752 if (entry->key_data == NULL) {
756 for (i = 0; i < entry->n_key_data; i++)
757 if (ret = krb5_copy_key_data_contents(handle->context,
759 &entry->key_data[i]))
765 * If KADM5_API_VERSION_1, we return an allocated structure, and
766 * we need to convert the new structure back into the format the
767 * caller is expecting.
769 if (handle->api_version == KADM5_API_VERSION_1) {
770 kadm5_principal_ent_t_v1 newv1;
772 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
778 newv1->principal = entry->principal;
779 newv1->princ_expire_time = entry->princ_expire_time;
780 newv1->last_pwd_change = entry->last_pwd_change;
781 newv1->pw_expiration = entry->pw_expiration;
782 newv1->max_life = entry->max_life;
783 newv1->mod_name = entry->mod_name;
784 newv1->mod_date = entry->mod_date;
785 newv1->attributes = entry->attributes;
786 newv1->kvno = entry->kvno;
787 newv1->mkvno = entry->mkvno;
788 newv1->policy = entry->policy;
789 newv1->aux_attributes = entry->aux_attributes;
791 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
797 if (ret && entry->principal)
798 krb5_free_principal(handle->context, entry->principal);
799 kdb_free_entry(handle, &kdb, &adb);
805 * Function: check_pw_reuse
807 * Purpose: Check if a key appears in a list of keys, in order to
808 * enforce password history.
812 * context (r) the krb5 context
813 * histkey_encblock (r) the encblock that hist_key_data is
815 * n_new_key_data (r) length of new_key_data
816 * new_key_data (r) keys to check against
817 * pw_hist_data, encrypted in histkey_encblock
818 * n_pw_hist_data (r) length of pw_hist_data
819 * pw_hist_data (r) passwords to check new_key_data against
822 * For each new_key in new_key_data:
823 * decrypt new_key with the master_encblock
824 * for each password in pw_hist_data:
825 * for each hist_key in password:
826 * decrypt hist_key with histkey_encblock
827 * compare the new_key and hist_key
829 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
830 * new_key_data is the same as a key in pw_hist_data, or 0.
833 check_pw_reuse(krb5_context context,
834 krb5_encrypt_block *histkey_encblock,
835 int n_new_key_data, krb5_key_data *new_key_data,
836 int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
839 krb5_keyblock newkey, histkey;
842 for (x = 0; x < n_new_key_data; x++) {
843 if (ret = krb5_dbekd_decrypt_key_data(context,
848 for (y = 0; y < n_pw_hist_data; y++) {
849 for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
851 krb5_dbekd_decrypt_key_data(context,
853 &pw_hist_data[y].key_data[z],
857 if ((newkey.length == histkey.length) &&
858 (newkey.enctype == histkey.enctype) &&
859 (memcmp(newkey.contents, histkey.contents,
860 histkey.length) == 0)) {
861 krb5_free_keyblock_contents(context, &histkey);
862 krb5_free_keyblock_contents(context, &newkey);
864 return(KADM5_PASS_REUSE);
866 krb5_free_keyblock_contents(context, &histkey);
869 krb5_free_keyblock_contents(context, &newkey);
876 * Function: create_history_entry
878 * Purpose: Creates a password history entry from an array of
883 * context (r) krb5_context to use
884 * n_key_data (r) number of elements in key_data
885 * key_data (r) keys to add to the history entry
886 * hist (w) history entry to fill in
890 * hist->key_data is allocated to store n_key_data key_datas. Each
891 * element of key_data is decrypted with master_encblock, re-encrypted
892 * in hist_encblock, and added to hist->key_data. hist->n_key_data is
895 int create_history_entry(krb5_context context, int n_key_data,
896 krb5_key_data *key_data, osa_pw_hist_ent *hist)
902 hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
903 if (hist->key_data == NULL)
905 memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
907 for (i = 0; i < n_key_data; i++) {
908 if (ret = krb5_dbekd_decrypt_key_data(context,
913 if (ret = krb5_dbekd_encrypt_key_data(context,
916 key_data[i].key_data_kvno,
919 krb5_free_keyblock_contents(context, &key);
920 /* krb5_free_keysalt(context, &salt); */
923 hist->n_key_data = n_key_data;
927 int free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
931 for (i = 0; i < hist->n_key_data; i++)
932 krb5_free_key_data_contents(context, &hist->key_data[i]);
933 free(hist->key_data);
937 * Function: add_to_history
939 * Purpose: Adds a password to a principal's password history.
943 * context (r) krb5_context to use
944 * adb (r/w) admin principal entry to add keys to
945 * pol (r) adb's policy
946 * pw (r) keys for the password to add to adb's key history
950 * add_to_history adds a single password to adb's password history.
951 * pw contains n_key_data keys in its key_data, in storage should be
952 * allocated but not freed by the caller (XXX blech!).
954 * This function maintains adb->old_keys as a circular queue. It
955 * starts empty, and grows each time this function is called until it
956 * is pol->pw_history_num items long. adb->old_key_len holds the
957 * number of allocated entries in the array, and must therefore be [0,
958 * pol->pw_history_num). adb->old_key_next is the index into the
959 * array where the next element should be written, and must be [0,
962 static kadm5_ret_t add_to_history(krb5_context context,
964 kadm5_policy_ent_t pol,
967 osa_pw_hist_ent hist, *histp;
970 /* A history of 1 means just check the current password */
971 if (pol->pw_history_num == 1)
974 /* resize the adb->old_keys array if necessary */
975 if (adb->old_key_len < pol->pw_history_num-1) {
976 adb->old_keys = (osa_pw_hist_ent *)
977 realloc(adb->old_keys,
978 (adb->old_key_len+1)*sizeof(osa_pw_hist_ent));
979 if (adb->old_keys == NULL)
982 memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent));
986 /* free the old pw history entry if it contains data */
987 histp = &adb->old_keys[adb->old_key_next];
988 for (i = 0; i < histp->n_key_data; i++)
989 krb5_free_key_data_contents(context, &histp->key_data[i]);
991 /* store the new entry */
992 adb->old_keys[adb->old_key_next] = *pw;
994 /* update the next pointer */
995 if (++adb->old_key_next == pol->pw_history_num-1)
996 adb->old_key_next = 0;
1002 kadm5_chpass_principal(void *server_handle,
1003 krb5_principal principal, char *password)
1006 kadm5_policy_ent_rec pol;
1007 osa_princ_ent_rec adb;
1008 krb5_db_entry kdb, kdb_save;
1009 int ret, ret2, last_pwd, i, hist_added;
1011 kadm5_server_handle_t handle = server_handle;
1012 osa_pw_hist_ent hist;
1014 CHECK_HANDLE(server_handle);
1017 memset(&hist, 0, sizeof(hist));
1019 if (principal == NULL || password == NULL)
1021 if ((krb5_principal_compare(handle->context,
1022 principal, hist_princ)) == TRUE)
1023 return KADM5_PROTECT_PRINCIPAL;
1025 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1028 /* we are going to need the current keys after the new keys are set */
1029 if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1030 kdb_free_entry(handle, &kdb, &adb);
1034 if ((adb.aux_attributes & KADM5_POLICY)) {
1035 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1040 if ((ret = passwd_check(handle, password, adb.aux_attributes &
1041 KADM5_POLICY, &pol, principal)))
1044 if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
1045 handle->params.keysalts,
1046 handle->params.num_keysalts,
1047 password, 0 /* increment kvno */, &kdb))
1050 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1052 if (ret = krb5_timeofday(handle->context, &now))
1055 if ((adb.aux_attributes & KADM5_POLICY)) {
1056 /* the policy was loaded before */
1058 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1064 * The spec says this check is overridden if the caller has
1065 * modify privilege. The admin server therefore makes this
1066 * check itself (in chpass_principal_wrapper, misc.c). A
1067 * local caller implicitly has all authorization bits.
1069 if ((now - last_pwd) < pol.pw_min_life &&
1070 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1071 ret = KADM5_PASS_TOOSOON;
1076 if (ret = create_history_entry(handle->context,
1077 kdb_save.n_key_data,
1078 kdb_save.key_data, &hist))
1081 if (ret = check_pw_reuse(handle->context,
1083 kdb.n_key_data, kdb.key_data,
1087 if (pol.pw_history_num > 1) {
1088 if (adb.admin_history_kvno != hist_kvno) {
1089 ret = KADM5_BAD_HIST_KEY;
1093 if (ret = check_pw_reuse(handle->context,
1095 kdb.n_key_data, kdb.key_data,
1096 adb.old_key_len, adb.old_keys))
1099 if (ret = add_to_history(handle->context, &adb, &pol, &hist))
1104 if (pol.pw_max_life)
1105 kdb.pw_expiration = now + pol.pw_max_life;
1107 kdb.pw_expiration = 0;
1109 kdb.pw_expiration = 0;
1112 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1115 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1120 if (!hist_added && hist.key_data)
1121 free_history_entry(handle->context, &hist);
1122 kdb_free_entry(handle, &kdb, &adb);
1123 kdb_free_entry(handle, &kdb_save, NULL);
1124 krb5_dbe_free_contents(handle->context, &kdb);
1126 if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1134 kadm5_randkey_principal(void *server_handle,
1135 krb5_principal principal,
1136 krb5_keyblock **keyblocks,
1140 osa_princ_ent_rec adb;
1142 kadm5_policy_ent_rec pol;
1143 krb5_key_data *key_data;
1144 krb5_keyblock *keyblock;
1145 int ret, last_pwd, have_pol = 0;
1146 kadm5_server_handle_t handle = server_handle;
1151 CHECK_HANDLE(server_handle);
1153 if (principal == NULL)
1155 if (hist_princ && /* this will be NULL when initializing the databse */
1156 ((krb5_principal_compare(handle->context,
1157 principal, hist_princ)) == TRUE))
1158 return KADM5_PROTECT_PRINCIPAL;
1160 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1163 if (ret = krb5_dbe_crk(handle->context, &master_encblock,
1164 handle->params.keysalts,
1165 handle->params.num_keysalts,
1169 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1171 if (ret = krb5_timeofday(handle->context, &now))
1174 if ((adb.aux_attributes & KADM5_POLICY)) {
1175 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1180 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1186 * The spec says this check is overridden if the caller has
1187 * modify privilege. The admin server therefore makes this
1188 * check itself (in chpass_principal_wrapper, misc.c). A
1189 * local caller implicitly has all authorization bits.
1191 if((now - last_pwd) < pol.pw_min_life &&
1192 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1193 ret = KADM5_PASS_TOOSOON;
1198 if(pol.pw_history_num > 1) {
1199 if(adb.admin_history_kvno != hist_kvno) {
1200 ret = KADM5_BAD_HIST_KEY;
1204 if (ret = check_pw_reuse(handle->context,
1206 kdb.n_key_data, kdb.key_data,
1207 adb.old_key_len, adb.old_keys))
1210 if (pol.pw_max_life)
1211 kdb.pw_expiration = now + pol.pw_max_life;
1213 kdb.pw_expiration = 0;
1215 kdb.pw_expiration = 0;
1218 if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1222 if (handle->api_version == KADM5_API_VERSION_1) {
1223 /* Version 1 clients will expect to see a DES_CRC enctype. */
1224 if (ret = krb5_dbe_find_enctype(handle->context, &kdb,
1225 ENCTYPE_DES_CBC_CRC,
1229 if (ret = decrypt_key_data(handle->context, 1, key_data,
1233 ret = decrypt_key_data(handle->context,
1234 kdb.n_key_data, kdb.key_data,
1241 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1246 kdb_free_entry(handle, &kdb, &adb);
1248 kadm5_free_policy_ent(handle->lhandle, &pol);
1254 * Allocate an array of n_key_data krb5_keyblocks, fill in each
1255 * element with the results of decrypting the nth key in key_data with
1256 * master_encblock, and if n_keys is not NULL fill it in with the
1257 * number of keys decrypted.
1259 static int decrypt_key_data(krb5_context context,
1260 int n_key_data, krb5_key_data *key_data,
1261 krb5_keyblock **keyblocks, int *n_keys)
1263 krb5_keyblock *keys;
1266 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1269 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1271 for (i = 0; i < n_key_data; i++) {
1272 if (ret = krb5_dbekd_decrypt_key_data(context,
1277 memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1285 *n_keys = n_key_data;
1291 * Function: kadm5_decrypt_key
1293 * Purpose: Retrieves and decrypts a principal key.
1297 * server_handle (r) kadm5 handle
1298 * entry (r) principal retrieved with kadm5_get_principal
1299 * ktype (r) enctype to search for, or -1 to ignore
1300 * stype (r) salt type to search for, or -1 to ignore
1301 * kvno (r) kvno to search for, -1 for max, 0 for max
1302 * only if it also matches ktype and stype
1303 * keyblock (w) keyblock to fill in
1304 * keysalt (w) keysalt to fill in, or NULL
1305 * kvnop (w) kvno to fill in, or NULL
1307 * Effects: Searches the key_data array of entry, which must have been
1308 * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1309 * find a key with a specified enctype, salt type, and kvno in a
1310 * principal entry. If not found, return ENOENT. Otherwise, decrypt
1311 * it with the master key, and return the key in keyblock, the salt
1312 * in salttype, and the key version number in kvno.
1314 * If ktype or stype is -1, it is ignored for the search. If kvno is
1315 * -1, ktype and stype are ignored and the key with the max kvno is
1316 * returned. If kvno is 0, only the key with the max kvno is returned
1317 * and only if it matches the ktype and stype; otherwise, ENOENT is
1320 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1321 kadm5_principal_ent_t entry, krb5_int32
1322 ktype, krb5_int32 stype, krb5_int32
1323 kvno, krb5_keyblock *keyblock,
1324 krb5_keysalt *keysalt, int *kvnop)
1326 kadm5_server_handle_t handle = server_handle;
1327 krb5_db_entry dbent;
1328 krb5_key_data *key_data;
1331 CHECK_HANDLE(server_handle);
1333 if (entry->n_key_data == 0 || entry->key_data == NULL)
1336 /* find_enctype only uses these two fields */
1337 dbent.n_key_data = entry->n_key_data;
1338 dbent.key_data = entry->key_data;
1339 if (ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1340 stype, kvno, &key_data))
1343 if (ret = krb5_dbekd_decrypt_key_data(handle->context,
1344 &master_encblock, key_data,
1349 *kvnop = key_data->key_data_kvno;