1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
9 #include <kadm5/admin.h>
11 #include "server_internal.h"
12 #ifdef USE_PASSWORD_SERVER
17 #include <krb5/kadm5_hook_plugin.h>
20 #include <valgrind/memcheck.h>
22 #define VALGRIND_CHECK_DEFINED(LVALUE) ((void)0)
25 extern krb5_principal master_princ;
26 extern krb5_principal hist_princ;
27 extern krb5_keyblock master_keyblock;
28 extern krb5_keylist_node *master_keylist;
29 extern krb5_actkvno_node *active_mkey_list;
30 extern krb5_db_entry master_db;
32 static int decrypt_key_data(krb5_context context,
33 int n_key_data, krb5_key_data *key_data,
34 krb5_keyblock **keyblocks, int *n_keys);
36 static krb5_error_code
37 kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
39 register krb5_principal tempprinc;
40 register int i, nelems;
42 tempprinc = (krb5_principal)krb5_db_alloc(context, NULL, sizeof(krb5_principal_data));
47 VALGRIND_CHECK_DEFINED(*inprinc);
48 *tempprinc = *inprinc;
50 nelems = (int) krb5_princ_size(context, inprinc);
51 tempprinc->data = krb5_db_alloc(context, NULL, nelems * sizeof(krb5_data));
52 if (tempprinc->data == 0) {
53 krb5_db_free(context, (char *)tempprinc);
57 for (i = 0; i < nelems; i++) {
58 unsigned int len = krb5_princ_component(context, inprinc, i)->length;
59 krb5_princ_component(context, tempprinc, i)->length = len;
60 if (((krb5_princ_component(context, tempprinc, i)->data =
61 krb5_db_alloc(context, NULL, len)) == 0) && len) {
63 krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
64 krb5_db_free (context, tempprinc->data);
65 krb5_db_free (context, tempprinc);
69 memcpy(krb5_princ_component(context, tempprinc, i)->data,
70 krb5_princ_component(context, inprinc, i)->data, len);
71 krb5_princ_component(context, tempprinc, i)->magic = KV5M_DATA;
74 tempprinc->realm.data =
75 krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length);
76 if (!tempprinc->realm.data && tempprinc->realm.length) {
77 for (i = 0; i < nelems; i++)
78 krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
79 krb5_db_free(context, tempprinc->data);
80 krb5_db_free(context, tempprinc);
83 if (tempprinc->realm.length)
84 memcpy(tempprinc->realm.data, inprinc->realm.data,
85 inprinc->realm.length);
87 *outprinc = tempprinc;
92 kadm5_free_principal(krb5_context context, krb5_principal val)
94 register krb5_int32 i;
100 i = krb5_princ_size(context, val);
102 krb5_db_free(context, krb5_princ_component(context, val, i)->data);
103 krb5_db_free(context, val->data);
106 krb5_db_free(context, val->realm.data);
107 krb5_db_free(context, val);
111 * XXX Functions that ought to be in libkrb5.a, but aren't.
113 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
114 krb5_context context;
115 krb5_key_data *from, *to;
121 idx = (from->key_data_ver == 1 ? 1 : 2);
123 for (i = 0; i < idx; i++) {
124 if ( from->key_data_length[i] ) {
125 to->key_data_contents[i] = malloc(from->key_data_length[i]);
126 if (to->key_data_contents[i] == NULL) {
127 for (i = 0; i < idx; i++) {
128 if (to->key_data_contents[i]) {
129 memset(to->key_data_contents[i], 0,
130 to->key_data_length[i]);
131 free(to->key_data_contents[i]);
136 memcpy(to->key_data_contents[i], from->key_data_contents[i],
137 from->key_data_length[i]);
143 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
147 n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
150 n->tl_data_contents = malloc(tl->tl_data_length);
151 if (n->tl_data_contents == NULL) {
155 memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
156 n->tl_data_type = tl->tl_data_type;
157 n->tl_data_length = tl->tl_data_length;
158 n->tl_data_next = NULL;
162 /* This is in lib/kdb/kdb_cpw.c, but is static */
163 static void cleanup_key_data(context, count, data)
164 krb5_context context;
166 krb5_key_data * data;
170 for (i = 0; i < count; i++)
171 for (j = 0; j < data[i].key_data_ver; j++)
172 if (data[i].key_data_length[j])
173 krb5_db_free(context, data[i].key_data_contents[j]);
174 krb5_db_free(context, data);
178 * Set *passptr to NULL if the request looks like the first part of a krb5 1.6
179 * addprinc -randkey operation. The krb5 1.6 dummy password for these requests
180 * was invalid UTF-8, which runs afoul of the arcfour string-to-key.
183 check_1_6_dummy(kadm5_principal_ent_t entry, long mask,
184 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char **passptr)
187 char *password = *passptr;
189 /* Old-style randkey operations disallowed tickets to start. */
190 if (!(mask & KADM5_ATTRIBUTES) ||
191 !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX))
194 /* The 1.6 dummy password was the octets 1..255. */
195 for (i = 0; (unsigned char) password[i] == i + 1; i++);
196 if (password[i] != '\0' || i != 255)
199 /* This will make the caller use a random password instead. */
204 kadm5_create_principal(void *server_handle,
205 kadm5_principal_ent_t entry, long mask,
209 kadm5_create_principal_3(server_handle, entry, mask,
213 kadm5_create_principal_3(void *server_handle,
214 kadm5_principal_ent_t entry, long mask,
215 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
219 osa_princ_ent_rec adb;
220 kadm5_policy_ent_rec polent;
221 krb5_boolean have_polent = FALSE;
223 krb5_tl_data *tl_data_orig, *tl_data_tail;
225 kadm5_server_handle_t handle = server_handle;
226 krb5_keyblock *act_mkey;
229 CHECK_HANDLE(server_handle);
231 krb5_clear_error_message(handle->context);
233 check_1_6_dummy(entry, mask, n_ks_tuple, ks_tuple, &password);
236 * Argument sanity checking, and opening up the DB
238 if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
239 (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
240 (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
241 (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
242 (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
243 (mask & KADM5_FAIL_AUTH_COUNT))
244 return KADM5_BAD_MASK;
245 if((mask & ~ALL_PRINC_MASK))
246 return KADM5_BAD_MASK;
250 /* Use default keysalts if caller did not provide any. */
251 if (n_ks_tuple == 0) {
252 ks_tuple = handle->params.keysalts;
253 n_ks_tuple = handle->params.num_keysalts;
257 * Check to see if the principal exists
259 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
262 case KADM5_UNK_PRINC:
265 kdb_free_entry(handle, kdb, &adb);
271 kdb = krb5_db_alloc(handle->context, NULL, sizeof(*kdb));
274 memset(kdb, 0, sizeof(*kdb));
275 memset(&adb, 0, sizeof(osa_princ_ent_rec));
278 * If a policy was specified, load it.
279 * If we can not find the one specified return an error
281 if ((mask & KADM5_POLICY)) {
282 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
283 &polent)) != KADM5_OK) {
285 ret = KADM5_BAD_POLICY;
292 ret = passwd_check(handle, password, have_polent ? &polent : NULL,
298 * Start populating the various DB fields, using the
299 * "defaults" for fields that were not specified by the
302 if ((ret = krb5_timeofday(handle->context, &now)))
305 kdb->magic = KRB5_KDB_MAGIC_NUMBER;
306 kdb->len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
308 if ((mask & KADM5_ATTRIBUTES))
309 kdb->attributes = entry->attributes;
311 kdb->attributes = handle->params.flags;
313 if ((mask & KADM5_MAX_LIFE))
314 kdb->max_life = entry->max_life;
316 kdb->max_life = handle->params.max_life;
318 if (mask & KADM5_MAX_RLIFE)
319 kdb->max_renewable_life = entry->max_renewable_life;
321 kdb->max_renewable_life = handle->params.max_rlife;
323 if ((mask & KADM5_PRINC_EXPIRE_TIME))
324 kdb->expiration = entry->princ_expire_time;
326 kdb->expiration = handle->params.expiration;
328 kdb->pw_expiration = 0;
330 if(polent.pw_max_life)
331 kdb->pw_expiration = now + polent.pw_max_life;
333 kdb->pw_expiration = 0;
335 if ((mask & KADM5_PW_EXPIRATION))
336 kdb->pw_expiration = entry->pw_expiration;
338 kdb->last_success = 0;
339 kdb->last_failed = 0;
340 kdb->fail_auth_count = 0;
342 /* this is kind of gross, but in order to free the tl data, I need
343 to free the entire kdb entry, and that will try to free the
346 if ((ret = kadm5_copy_principal(handle->context,
347 entry->principal, &(kdb->princ))))
350 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
353 if (mask & KADM5_TL_DATA) {
354 /* splice entry->tl_data onto the front of kdb->tl_data */
355 tl_data_orig = kdb->tl_data;
356 for (tl_data_tail = entry->tl_data; tl_data_tail;
357 tl_data_tail = tl_data_tail->tl_data_next)
359 ret = krb5_dbe_update_tl_data(handle->context, kdb, tl_data_tail);
365 /* initialize the keys */
367 ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
368 active_mkey_list, &act_kvno, &act_mkey);
373 ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
374 password, (mask & KADM5_KVNO)?entry->kvno:1,
377 /* Null password means create with random key (new in 1.8). */
378 ret = krb5_dbe_crk(handle->context, &master_keyblock,
379 ks_tuple, n_ks_tuple, FALSE, kdb);
384 /* Record the master key VNO used to encrypt this entry's keys */
385 ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
389 ret = k5_kadm5_hook_create(handle->context, handle->hook_handles,
390 KADM5_HOOK_STAGE_PRECOMMIT, entry, mask,
391 n_ks_tuple, ks_tuple, password);
395 /* populate the admin-server-specific fields. In the OV server,
396 this used to be in a separate database. Since there's already
397 marshalling code for the admin fields, to keep things simple,
398 I'm going to keep it, and make all the admin stuff occupy a
399 single tl_data record, */
401 adb.admin_history_kvno = INITIAL_HIST_KVNO;
403 adb.aux_attributes = KADM5_POLICY;
405 /* this does *not* need to be strdup'ed, because adb is xdr */
406 /* encoded in osa_adb_create_princ, and not ever freed */
408 adb.policy = entry->policy;
411 /* increment the policy ref count, if any */
414 polent.policy_refcnt++;
415 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
421 /* In all cases key and the principal data is set, let the database provider know */
422 kdb->mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ;
424 /* store the new db entry */
425 ret = kdb_put_entry(handle, kdb, &adb);
430 /* decrement the policy ref count */
432 polent.policy_refcnt--;
434 * if this fails, there's nothing we can do anyway. the
435 * policy refcount wil be too high.
437 (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
442 (void) k5_kadm5_hook_create(handle->context, handle->hook_handles,
443 KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask,
444 n_ks_tuple, ks_tuple, password);
447 krb5_db_free_principal(handle->context, kdb);
449 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
455 kadm5_delete_principal(void *server_handle, krb5_principal principal)
458 kadm5_policy_ent_rec polent;
460 osa_princ_ent_rec adb;
461 kadm5_server_handle_t handle = server_handle;
463 CHECK_HANDLE(server_handle);
465 krb5_clear_error_message(handle->context);
467 if (principal == NULL)
470 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
472 ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
473 KADM5_HOOK_STAGE_PRECOMMIT, principal);
475 kdb_free_entry(handle, kdb, &adb);
479 if ((adb.aux_attributes & KADM5_POLICY)) {
480 if ((ret = kadm5_get_policy(handle->lhandle,
481 adb.policy, &polent))
483 polent.policy_refcnt--;
484 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
487 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
488 kdb_free_entry(handle, kdb, &adb);
492 if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
493 kdb_free_entry(handle, kdb, &adb);
498 ret = kdb_delete_entry(handle, principal);
500 kdb_free_entry(handle, kdb, &adb);
503 (void) k5_kadm5_hook_remove(handle->context,
504 handle->hook_handles,
505 KADM5_HOOK_STAGE_POSTCOMMIT, principal);
511 kadm5_modify_principal(void *server_handle,
512 kadm5_principal_ent_t entry, long mask)
515 kadm5_policy_ent_rec npol, opol;
516 int have_npol = 0, have_opol = 0;
518 krb5_tl_data *tl_data_orig;
519 osa_princ_ent_rec adb;
520 kadm5_server_handle_t handle = server_handle;
522 CHECK_HANDLE(server_handle);
524 krb5_clear_error_message(handle->context);
526 if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
527 (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
528 (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
529 (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
530 (mask & KADM5_LAST_FAILED))
531 return KADM5_BAD_MASK;
532 if((mask & ~ALL_PRINC_MASK))
533 return KADM5_BAD_MASK;
534 if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
535 return KADM5_BAD_MASK;
536 if(entry == (kadm5_principal_ent_t) NULL)
538 if (mask & KADM5_TL_DATA) {
539 tl_data_orig = entry->tl_data;
540 while (tl_data_orig) {
541 if (tl_data_orig->tl_data_type < 256)
542 return KADM5_BAD_TL_TYPE;
543 tl_data_orig = tl_data_orig->tl_data_next;
547 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
552 * This is pretty much the same as create ...
555 if ((mask & KADM5_POLICY)) {
556 /* get the new policy */
557 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
561 ret = KADM5_BAD_POLICY;
563 case KADM5_UNK_POLICY:
564 case KADM5_BAD_POLICY:
565 ret = KADM5_UNK_POLICY;
572 /* if we already have a policy, get it to decrement the refcnt */
573 if(adb.aux_attributes & KADM5_POLICY) {
574 /* ... but not if the old and new are the same */
575 if(strcmp(adb.policy, entry->policy)) {
576 ret = kadm5_get_policy(handle->lhandle,
580 case KADM5_BAD_POLICY:
581 case KADM5_UNK_POLICY:
585 opol.policy_refcnt--;
591 npol.policy_refcnt++;
593 } else npol.policy_refcnt++;
595 /* set us up to use the new policy */
596 adb.aux_attributes |= KADM5_POLICY;
599 adb.policy = strdup(entry->policy);
601 /* set pw_max_life based on new policy */
602 if (npol.pw_max_life) {
603 ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
604 &(kdb->pw_expiration));
607 kdb->pw_expiration += npol.pw_max_life;
609 kdb->pw_expiration = 0;
613 if ((mask & KADM5_POLICY_CLR) &&
614 (adb.aux_attributes & KADM5_POLICY)) {
615 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
618 case KADM5_BAD_POLICY:
619 case KADM5_UNK_POLICY:
628 adb.aux_attributes &= ~KADM5_POLICY;
629 kdb->pw_expiration = 0;
630 opol.policy_refcnt--;
638 if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
641 kadm5_modify_policy_internal(handle->lhandle, &opol,
642 KADM5_REF_COUNT))) ||
645 kadm5_modify_policy_internal(handle->lhandle, &npol,
649 if ((mask & KADM5_ATTRIBUTES))
650 kdb->attributes = entry->attributes;
651 if ((mask & KADM5_MAX_LIFE))
652 kdb->max_life = entry->max_life;
653 if ((mask & KADM5_PRINC_EXPIRE_TIME))
654 kdb->expiration = entry->princ_expire_time;
655 if (mask & KADM5_PW_EXPIRATION)
656 kdb->pw_expiration = entry->pw_expiration;
657 if (mask & KADM5_MAX_RLIFE)
658 kdb->max_renewable_life = entry->max_renewable_life;
660 if((mask & KADM5_KVNO)) {
661 for (i = 0; i < kdb->n_key_data; i++)
662 kdb->key_data[i].key_data_kvno = entry->kvno;
665 if (mask & KADM5_TL_DATA) {
668 /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */
670 for (tl = entry->tl_data; tl;
671 tl = tl->tl_data_next)
673 ret = krb5_dbe_update_tl_data(handle->context, kdb, tl);
682 * Setting entry->fail_auth_count to 0 can be used to manually unlock
683 * an account. It is not possible to set fail_auth_count to any other
684 * value using kadmin.
686 if (mask & KADM5_FAIL_AUTH_COUNT) {
687 if (entry->fail_auth_count != 0) {
688 ret = KADM5_BAD_SERVER_PARAMS;
692 kdb->fail_auth_count = 0;
695 /* let the mask propagate to the database provider */
698 ret = k5_kadm5_hook_modify(handle->context, handle->hook_handles,
699 KADM5_HOOK_STAGE_PRECOMMIT, entry, mask);
703 ret = kdb_put_entry(handle, kdb, &adb);
705 (void) k5_kadm5_hook_modify(handle->context, handle->hook_handles,
706 KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask);
711 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
712 ret = ret ? ret : ret2;
715 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
716 ret = ret ? ret : ret2;
718 kdb_free_entry(handle, kdb, &adb);
723 kadm5_rename_principal(void *server_handle,
724 krb5_principal source, krb5_principal target)
727 osa_princ_ent_rec adb;
729 kadm5_server_handle_t handle = server_handle;
733 CHECK_HANDLE(server_handle);
735 krb5_clear_error_message(handle->context);
737 if (source == NULL || target == NULL)
740 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
741 kdb_free_entry(handle, kdb, &adb);
745 if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
748 /* Transform salts as necessary. */
749 for (i = 0; i < kdb->n_key_data; i++) {
750 sdata = empty_data();
751 if (kdb->key_data[i].key_data_ver > 1)
752 stype = kdb->key_data[i].key_data_type[1];
754 stype = KRB5_KDB_SALTTYPE_NORMAL;
756 /* For salt types which compute a salt from the principal name, compute
757 * the salt based on the old principal name into sdata. */
759 case KRB5_KDB_SALTTYPE_NORMAL:
760 ret = krb5_principal2salt(handle->context, kdb->princ, &sdata);
764 case KRB5_KDB_SALTTYPE_NOREALM:
765 krb5_principal2salt_norealm(handle->context, kdb->princ, &sdata);
769 case KRB5_KDB_SALTTYPE_ONLYREALM:
770 ret = alloc_data(&sdata, kdb->princ->realm.length);
773 memcpy(sdata.data, kdb->princ->realm.data,
774 kdb->princ->realm.length);
776 case KRB5_KDB_SALTTYPE_SPECIAL:
777 case KRB5_KDB_SALTTYPE_V4:
778 case KRB5_KDB_SALTTYPE_AFS3:
779 /* Don't compute a new salt. Assume the realm doesn't change for
783 /* We don't recognize this salt type. Be conservative. */
784 ret = KADM5_NO_RENAME_SALT;
787 /* If we computed a salt, store it as an explicit salt. */
788 if (sdata.data != NULL) {
789 kdb->key_data[i].key_data_type[1] = KRB5_KDB_SALTTYPE_SPECIAL;
790 free(kdb->key_data[i].key_data_contents[1]);
791 kdb->key_data[i].key_data_contents[1] = (krb5_octet *)sdata.data;
792 kdb->key_data[i].key_data_length[1] = sdata.length;
793 kdb->key_data[i].key_data_ver = 2;
797 kadm5_free_principal(handle->context, kdb->princ);
798 ret = kadm5_copy_principal(handle->context, target, &kdb->princ);
800 kdb->princ = NULL; /* so freeing the dbe doesn't lose */
804 if ((ret = kdb_put_entry(handle, kdb, &adb)))
807 ret = kdb_delete_entry(handle, source);
810 kdb_free_entry(handle, kdb, &adb);
815 kadm5_get_principal(void *server_handle, krb5_principal principal,
816 kadm5_principal_ent_t entry,
820 osa_princ_ent_rec adb;
821 krb5_error_code ret = 0;
824 kadm5_server_handle_t handle = server_handle;
826 CHECK_HANDLE(server_handle);
828 krb5_clear_error_message(handle->context);
831 * In version 1, all the defined fields are always returned.
832 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
833 * filled with allocated memory.
837 memset(entry, 0, sizeof(*entry));
839 if (principal == NULL)
842 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
845 if ((mask & KADM5_POLICY) &&
846 adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
847 if ((entry->policy = strdup(adb.policy)) == NULL) {
853 if (mask & KADM5_AUX_ATTRIBUTES)
854 entry->aux_attributes = adb.aux_attributes;
856 if ((mask & KADM5_PRINCIPAL) &&
857 (ret = krb5_copy_principal(handle->context, kdb->princ,
858 &entry->principal))) {
862 if (mask & KADM5_PRINC_EXPIRE_TIME)
863 entry->princ_expire_time = kdb->expiration;
865 if ((mask & KADM5_LAST_PWD_CHANGE) &&
866 (ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
867 &(entry->last_pwd_change)))) {
871 if (mask & KADM5_PW_EXPIRATION)
872 entry->pw_expiration = kdb->pw_expiration;
873 if (mask & KADM5_MAX_LIFE)
874 entry->max_life = kdb->max_life;
876 /* this is a little non-sensical because the function returns two */
877 /* values that must be checked separately against the mask */
878 if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
879 ret = krb5_dbe_lookup_mod_princ_data(handle->context, kdb,
886 if (! (mask & KADM5_MOD_TIME))
888 if (! (mask & KADM5_MOD_NAME)) {
889 krb5_free_principal(handle->context, entry->mod_name);
890 entry->mod_name = NULL;
894 if (mask & KADM5_ATTRIBUTES)
895 entry->attributes = kdb->attributes;
897 if (mask & KADM5_KVNO)
898 for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++)
899 if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno)
900 entry->kvno = kdb->key_data[i].key_data_kvno;
902 if (mask & KADM5_MKVNO) {
903 ret = krb5_dbe_get_mkvno(handle->context, kdb, master_keylist,
909 if (mask & KADM5_MAX_RLIFE)
910 entry->max_renewable_life = kdb->max_renewable_life;
911 if (mask & KADM5_LAST_SUCCESS)
912 entry->last_success = kdb->last_success;
913 if (mask & KADM5_LAST_FAILED)
914 entry->last_failed = kdb->last_failed;
915 if (mask & KADM5_FAIL_AUTH_COUNT)
916 entry->fail_auth_count = kdb->fail_auth_count;
917 if (mask & KADM5_TL_DATA) {
918 krb5_tl_data *tl, *tl2;
920 entry->tl_data = NULL;
924 if (tl->tl_data_type > 255) {
925 if ((tl2 = dup_tl_data(tl)) == NULL) {
929 tl2->tl_data_next = entry->tl_data;
930 entry->tl_data = tl2;
934 tl = tl->tl_data_next;
937 if (mask & KADM5_KEY_DATA) {
938 entry->n_key_data = kdb->n_key_data;
939 if(entry->n_key_data) {
940 entry->key_data = malloc(entry->n_key_data*sizeof(krb5_key_data));
941 if (entry->key_data == NULL) {
946 entry->key_data = NULL;
948 for (i = 0; i < entry->n_key_data; i++)
949 ret = krb5_copy_key_data_contents(handle->context,
951 &entry->key_data[i]);
959 if (ret && entry->principal) {
960 krb5_free_principal(handle->context, entry->principal);
961 entry->principal = NULL;
963 kdb_free_entry(handle, kdb, &adb);
969 * Function: check_pw_reuse
971 * Purpose: Check if a key appears in a list of keys, in order to
972 * enforce password history.
976 * context (r) the krb5 context
977 * hist_keyblock (r) the key that hist_key_data is
979 * n_new_key_data (r) length of new_key_data
980 * new_key_data (r) keys to check against
981 * pw_hist_data, encrypted in hist_keyblock
982 * n_pw_hist_data (r) length of pw_hist_data
983 * pw_hist_data (r) passwords to check new_key_data against
986 * For each new_key in new_key_data:
987 * decrypt new_key with the master_keyblock
988 * for each password in pw_hist_data:
989 * for each hist_key in password:
990 * decrypt hist_key with hist_keyblock
991 * compare the new_key and hist_key
993 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
994 * new_key_data is the same as a key in pw_hist_data, or 0.
997 check_pw_reuse(krb5_context context,
998 krb5_keyblock *hist_keyblock,
999 int n_new_key_data, krb5_key_data *new_key_data,
1000 unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
1002 unsigned int x, y, z;
1003 krb5_keyblock newkey, histkey;
1004 krb5_error_code ret;
1006 assert (n_new_key_data >= 0);
1007 for (x = 0; x < (unsigned) n_new_key_data; x++) {
1008 ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]),
1012 for (y = 0; y < n_pw_hist_data; y++) {
1013 for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) {
1014 ret = krb5_dbe_decrypt_key_data(context, hist_keyblock,
1015 &pw_hist_data[y].key_data[z],
1020 if ((newkey.length == histkey.length) &&
1021 (newkey.enctype == histkey.enctype) &&
1022 (memcmp(newkey.contents, histkey.contents,
1023 histkey.length) == 0)) {
1024 krb5_free_keyblock_contents(context, &histkey);
1025 krb5_free_keyblock_contents(context, &newkey);
1027 return(KADM5_PASS_REUSE);
1029 krb5_free_keyblock_contents(context, &histkey);
1032 krb5_free_keyblock_contents(context, &newkey);
1039 * Function: create_history_entry
1041 * Purpose: Creates a password history entry from an array of
1046 * context (r) krb5_context to use
1047 * mkey (r) master keyblock to decrypt key data with
1048 * hist_key (r) history keyblock to encrypt key data with
1049 * n_key_data (r) number of elements in key_data
1050 * key_data (r) keys to add to the history entry
1051 * hist (w) history entry to fill in
1055 * hist->key_data is allocated to store n_key_data key_datas. Each
1056 * element of key_data is decrypted with master_keyblock, re-encrypted
1057 * in hist_key, and added to hist->key_data. hist->n_key_data is
1058 * set to n_key_data.
1061 int create_history_entry(krb5_context context,
1062 krb5_keyblock *hist_key, int n_key_data,
1063 krb5_key_data *key_data, osa_pw_hist_ent *hist)
1069 hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
1070 if (hist->key_data == NULL)
1072 memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
1074 for (i = 0; i < n_key_data; i++) {
1075 ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &key,
1080 ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
1081 key_data[i].key_data_kvno,
1082 &hist->key_data[i]);
1086 krb5_free_keyblock_contents(context, &key);
1087 /* krb5_free_keysalt(context, &salt); */
1090 hist->n_key_data = n_key_data;
1095 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
1099 for (i = 0; i < hist->n_key_data; i++)
1100 krb5_free_key_data_contents(context, &hist->key_data[i]);
1101 free(hist->key_data);
1105 * Function: add_to_history
1107 * Purpose: Adds a password to a principal's password history.
1111 * context (r) krb5_context to use
1112 * hist_kvno (r) kvno of current history key
1113 * adb (r/w) admin principal entry to add keys to
1114 * pol (r) adb's policy
1115 * pw (r) keys for the password to add to adb's key history
1119 * add_to_history adds a single password to adb's password history.
1120 * pw contains n_key_data keys in its key_data, in storage should be
1121 * allocated but not freed by the caller (XXX blech!).
1123 * This function maintains adb->old_keys as a circular queue. It
1124 * starts empty, and grows each time this function is called until it
1125 * is pol->pw_history_num items long. adb->old_key_len holds the
1126 * number of allocated entries in the array, and must therefore be [0,
1127 * pol->pw_history_num). adb->old_key_next is the index into the
1128 * array where the next element should be written, and must be [0,
1129 * adb->old_key_len).
1131 static kadm5_ret_t add_to_history(krb5_context context,
1132 krb5_kvno hist_kvno,
1133 osa_princ_ent_t adb,
1134 kadm5_policy_ent_t pol,
1135 osa_pw_hist_ent *pw)
1137 osa_pw_hist_ent *histp;
1139 unsigned int i, knext, nkeys;
1141 nhist = pol->pw_history_num;
1142 /* A history of 1 means just check the current password */
1146 if (adb->admin_history_kvno != hist_kvno) {
1147 /* The history key has changed since the last password change, so we
1148 * have to reset the password history. */
1149 free(adb->old_keys);
1150 adb->old_keys = NULL;
1151 adb->old_key_len = 0;
1152 adb->old_key_next = 0;
1153 adb->admin_history_kvno = hist_kvno;
1156 nkeys = adb->old_key_len;
1157 knext = adb->old_key_next;
1158 /* resize the adb->old_keys array if necessary */
1159 if (nkeys + 1 < nhist) {
1160 if (adb->old_keys == NULL) {
1161 adb->old_keys = (osa_pw_hist_ent *)
1162 malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
1164 adb->old_keys = (osa_pw_hist_ent *)
1165 realloc(adb->old_keys,
1166 (nkeys + 1) * sizeof (osa_pw_hist_ent));
1168 if (adb->old_keys == NULL)
1171 memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
1172 nkeys = ++adb->old_key_len;
1174 * To avoid losing old keys, shift forward each entry after
1177 for (i = nkeys - 1; i > knext; i--) {
1178 adb->old_keys[i] = adb->old_keys[i - 1];
1180 memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
1181 } else if (nkeys + 1 > nhist) {
1183 * The policy must have changed! Shrink the array.
1184 * Can't simply realloc() down, since it might be wrapped.
1185 * To understand the arithmetic below, note that we are
1186 * copying into new positions 0 .. N-1 from old positions
1187 * old_key_next-N .. old_key_next-1, modulo old_key_len,
1188 * where N = pw_history_num - 1 is the length of the
1189 * shortened list. Matt Crawford, FNAL
1192 * M = adb->old_key_len, N = pol->pw_history_num - 1
1194 * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
1199 tmp = (osa_pw_hist_ent *)
1200 malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
1203 for (i = 0; i < nhist - 1; i++) {
1205 * Add nkeys once before taking remainder to avoid
1208 j = (i + nkeys + knext - (nhist - 1)) % nkeys;
1209 tmp[i] = adb->old_keys[j];
1211 /* Now free the ones we don't keep (the oldest ones) */
1212 for (i = 0; i < nkeys - (nhist - 1); i++) {
1213 j = (i + nkeys + knext) % nkeys;
1214 histp = &adb->old_keys[j];
1215 for (j = 0; j < histp->n_key_data; j++) {
1216 krb5_free_key_data_contents(context, &histp->key_data[j]);
1218 free(histp->key_data);
1220 free(adb->old_keys);
1221 adb->old_keys = tmp;
1222 nkeys = adb->old_key_len = nhist - 1;
1223 knext = adb->old_key_next = 0;
1227 * If nhist decreased since the last password change, and nkeys+1
1228 * is less than the previous nhist, it is possible for knext to
1229 * index into unallocated space. This condition would not be
1230 * caught by the resizing code above.
1232 if (knext + 1 > nkeys)
1233 knext = adb->old_key_next = 0;
1234 /* free the old pw history entry if it contains data */
1235 histp = &adb->old_keys[knext];
1236 for (i = 0; i < (unsigned int) histp->n_key_data; i++)
1237 krb5_free_key_data_contents(context, &histp->key_data[i]);
1238 free(histp->key_data);
1240 /* store the new entry */
1241 adb->old_keys[knext] = *pw;
1243 /* update the next pointer */
1244 if (++adb->old_key_next == nhist - 1)
1245 adb->old_key_next = 0;
1250 /* FIXME: don't use global variable for this */
1251 krb5_boolean use_password_server = 0;
1253 #ifdef USE_PASSWORD_SERVER
1255 kadm5_use_password_server (void)
1257 return use_password_server;
1262 kadm5_set_use_password_server (void)
1264 use_password_server = 1;
1267 #ifdef USE_PASSWORD_SERVER
1270 * kadm5_launch_task () runs a program (task_path) to synchronize the
1271 * Apple password server with the Kerberos database. Password server
1272 * programs can receive arguments on the command line (task_argv)
1273 * and a block of data via stdin (data_buffer).
1275 * Because a failure to communicate with the tool results in the
1276 * password server falling out of sync with the database,
1277 * kadm5_launch_task() always fails if it can't talk to the tool.
1281 kadm5_launch_task (krb5_context context,
1282 const char *task_path, char * const task_argv[],
1288 ret = pipe (data_pipe);
1293 pid_t pid = fork ();
1296 close (data_pipe[0]);
1297 close (data_pipe[1]);
1298 } else if (pid == 0) {
1301 if (dup2 (data_pipe[0], STDIN_FILENO) == -1)
1304 close (data_pipe[0]);
1305 close (data_pipe[1]);
1307 execv (task_path, task_argv);
1309 _exit (1); /* Fail if execv fails */
1316 close (data_pipe[0]);
1318 /* Write out the buffer to the child, add \n */
1320 if (krb5_net_write (context, data_pipe[1], buffer, strlen (buffer)) < 0
1321 || krb5_net_write (context, data_pipe[1], "\n", 1) < 0)
1323 /* kill the child to make sure waitpid() won't hang later */
1325 kill (pid, SIGKILL);
1328 close (data_pipe[1]);
1330 waitpid (pid, &status, 0);
1333 if (WIFEXITED (status)) {
1334 /* child read password and exited. Check the return value. */
1335 if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
1336 ret = KRB5KDC_ERR_POLICY; /* password change rejected */
1339 /* child read password but crashed or was killed */
1340 ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1352 kadm5_chpass_principal(void *server_handle,
1353 krb5_principal principal, char *password)
1356 kadm5_chpass_principal_3(server_handle, principal, FALSE,
1361 kadm5_chpass_principal_3(void *server_handle,
1362 krb5_principal principal, krb5_boolean keepold,
1363 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1367 kadm5_policy_ent_rec pol;
1368 osa_princ_ent_rec adb;
1369 krb5_db_entry *kdb, *kdb_save;
1370 int ret, ret2, last_pwd, hist_added;
1372 kadm5_server_handle_t handle = server_handle;
1373 osa_pw_hist_ent hist;
1374 krb5_keyblock *act_mkey, hist_keyblock;
1375 krb5_kvno act_kvno, hist_kvno;
1377 CHECK_HANDLE(server_handle);
1379 krb5_clear_error_message(handle->context);
1382 memset(&hist, 0, sizeof(hist));
1383 memset(&hist_keyblock, 0, sizeof(hist_keyblock));
1385 if (principal == NULL || password == NULL)
1387 if ((krb5_principal_compare(handle->context,
1388 principal, hist_princ)) == TRUE)
1389 return KADM5_PROTECT_PRINCIPAL;
1391 /* Use default keysalts if caller did not provide any. */
1392 if (n_ks_tuple == 0) {
1393 ks_tuple = handle->params.keysalts;
1394 n_ks_tuple = handle->params.num_keysalts;
1397 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1400 /* we are going to need the current keys after the new keys are set */
1401 if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1402 kdb_free_entry(handle, kdb, &adb);
1406 if ((adb.aux_attributes & KADM5_POLICY)) {
1407 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1412 if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
1416 ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1417 active_mkey_list, &act_kvno, &act_mkey);
1421 ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
1422 password, 0 /* increment kvno */,
1427 ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
1431 kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1433 ret = krb5_timeofday(handle->context, &now);
1437 if ((adb.aux_attributes & KADM5_POLICY)) {
1438 /* the policy was loaded before */
1440 ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
1446 * The spec says this check is overridden if the caller has
1447 * modify privilege. The admin server therefore makes this
1448 * check itself (in chpass_principal_wrapper, misc.c). A
1449 * local caller implicitly has all authorization bits.
1451 if ((now - last_pwd) < pol.pw_min_life &&
1452 !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1453 ret = KADM5_PASS_TOOSOON;
1458 ret = kdb_get_hist_key(handle, &hist_keyblock, &hist_kvno);
1462 ret = create_history_entry(handle->context,
1464 kdb_save->n_key_data,
1465 kdb_save->key_data, &hist);
1469 ret = check_pw_reuse(handle->context, &hist_keyblock,
1470 kdb->n_key_data, kdb->key_data,
1475 if (pol.pw_history_num > 1) {
1476 /* If hist_kvno has changed since the last password change, we
1477 * can't check the history. */
1478 if (adb.admin_history_kvno == hist_kvno) {
1479 ret = check_pw_reuse(handle->context, &hist_keyblock,
1480 kdb->n_key_data, kdb->key_data,
1481 adb.old_key_len, adb.old_keys);
1486 ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
1493 if (pol.pw_max_life)
1494 kdb->pw_expiration = now + pol.pw_max_life;
1496 kdb->pw_expiration = 0;
1498 kdb->pw_expiration = 0;
1501 #ifdef USE_PASSWORD_SERVER
1502 if (kadm5_use_password_server () &&
1503 (krb5_princ_size (handle->context, principal) == 1)) {
1504 krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
1505 const char *path = "/usr/sbin/mkpassdb";
1506 char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
1507 char *pstring = NULL;
1510 pstring = malloc ((princ->length + 1) * sizeof (char));
1511 if (pstring == NULL) { ret = ENOMEM; }
1515 memcpy (pstring, princ->data, princ->length);
1516 pstring [princ->length] = '\0';
1519 ret = kadm5_launch_task (handle->context, path, argv, password);
1522 if (pstring != NULL)
1530 ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1534 /* unlock principal on this KDC */
1535 kdb->fail_auth_count = 0;
1537 /* key data and attributes changed, let the database provider know */
1538 kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES |
1539 KADM5_FAIL_AUTH_COUNT;
1540 /* | KADM5_CPW_FUNCTION */
1542 ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1543 KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1544 n_ks_tuple, ks_tuple, password);
1548 if ((ret = kdb_put_entry(handle, kdb, &adb)))
1551 (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1552 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1553 keepold, n_ks_tuple, ks_tuple, password);
1556 if (!hist_added && hist.key_data)
1557 free_history_entry(handle->context, &hist);
1558 kdb_free_entry(handle, kdb, &adb);
1559 kdb_free_entry(handle, kdb_save, NULL);
1560 krb5_free_keyblock_contents(handle->context, &hist_keyblock);
1562 if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1570 kadm5_randkey_principal(void *server_handle,
1571 krb5_principal principal,
1572 krb5_keyblock **keyblocks,
1576 kadm5_randkey_principal_3(server_handle, principal,
1581 kadm5_randkey_principal_3(void *server_handle,
1582 krb5_principal principal,
1583 krb5_boolean keepold,
1584 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1585 krb5_keyblock **keyblocks,
1589 osa_princ_ent_rec adb;
1591 kadm5_policy_ent_rec pol;
1592 int ret, last_pwd, have_pol = 0;
1593 kadm5_server_handle_t handle = server_handle;
1594 krb5_keyblock *act_mkey;
1599 CHECK_HANDLE(server_handle);
1601 /* Use default keysalts if caller did not provide any. */
1602 if (n_ks_tuple == 0) {
1603 ks_tuple = handle->params.keysalts;
1604 n_ks_tuple = handle->params.num_keysalts;
1607 krb5_clear_error_message(handle->context);
1609 if (principal == NULL)
1611 if (krb5_principal_compare(handle->context, principal, hist_princ)) {
1612 /* If changing the history entry, the new entry must have exactly one
1615 return KADM5_PROTECT_PRINCIPAL;
1619 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1622 ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1623 active_mkey_list, NULL, &act_mkey);
1627 ret = krb5_dbe_crk(handle->context, act_mkey, ks_tuple, n_ks_tuple,
1632 kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1634 ret = krb5_timeofday(handle->context, &now);
1638 if ((adb.aux_attributes & KADM5_POLICY)) {
1639 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1644 ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
1650 * The spec says this check is overridden if the caller has
1651 * modify privilege. The admin server therefore makes this
1652 * check itself (in chpass_principal_wrapper, misc.c). A
1653 * local caller implicitly has all authorization bits.
1655 if((now - last_pwd) < pol.pw_min_life &&
1656 !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1657 ret = KADM5_PASS_TOOSOON;
1662 if (pol.pw_max_life)
1663 kdb->pw_expiration = now + pol.pw_max_life;
1665 kdb->pw_expiration = 0;
1667 kdb->pw_expiration = 0;
1670 ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1674 /* unlock principal on this KDC */
1675 kdb->fail_auth_count = 0;
1678 ret = decrypt_key_data(handle->context,
1679 kdb->n_key_data, kdb->key_data,
1685 /* key data changed, let the database provider know */
1686 kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT;
1687 /* | KADM5_RANDKEY_USED */;
1689 ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1690 KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1691 n_ks_tuple, ks_tuple, NULL);
1694 if ((ret = kdb_put_entry(handle, kdb, &adb)))
1697 (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1698 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1699 keepold, n_ks_tuple, ks_tuple, NULL);
1702 kdb_free_entry(handle, kdb, &adb);
1704 kadm5_free_policy_ent(handle->lhandle, &pol);
1710 * kadm5_setv4key_principal:
1712 * Set only ONE key of the principal, removing all others. This key
1713 * must have the DES_CBC_CRC enctype and is entered as having the
1714 * krb4 salttype. This is to enable things like kadmind4 to work.
1717 kadm5_setv4key_principal(void *server_handle,
1718 krb5_principal principal,
1719 krb5_keyblock *keyblock)
1722 osa_princ_ent_rec adb;
1724 kadm5_policy_ent_rec pol;
1725 krb5_keysalt keysalt;
1726 int i, k, kvno, ret, have_pol = 0;
1730 kadm5_server_handle_t handle = server_handle;
1731 krb5_key_data tmp_key_data;
1732 krb5_keyblock *act_mkey;
1734 memset( &tmp_key_data, 0, sizeof(tmp_key_data));
1736 CHECK_HANDLE(server_handle);
1738 krb5_clear_error_message(handle->context);
1740 if (principal == NULL || keyblock == NULL)
1742 if (hist_princ && /* this will be NULL when initializing the databse */
1743 ((krb5_principal_compare(handle->context,
1744 principal, hist_princ)) == TRUE))
1745 return KADM5_PROTECT_PRINCIPAL;
1747 if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
1748 return KADM5_SETV4KEY_INVAL_ENCTYPE;
1750 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1753 for (kvno = 0, i=0; i<kdb->n_key_data; i++)
1754 if (kdb->key_data[i].key_data_kvno > kvno)
1755 kvno = kdb->key_data[i].key_data_kvno;
1757 if (kdb->key_data != NULL)
1758 cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1760 kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
1761 if (kdb->key_data == NULL)
1763 memset(kdb->key_data, 0, sizeof(krb5_key_data));
1764 kdb->n_key_data = 1;
1765 keysalt.type = KRB5_KDB_SALTTYPE_V4;
1766 /* XXX data.magic? */
1767 keysalt.data.length = 0;
1768 keysalt.data.data = NULL;
1770 ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1771 active_mkey_list, NULL, &act_mkey);
1775 /* use tmp_key_data as temporary location and reallocate later */
1776 ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, keyblock,
1777 &keysalt, kvno + 1, &tmp_key_data);
1782 for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1783 kdb->key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
1784 kdb->key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
1785 if (tmp_key_data.key_data_contents[k]) {
1786 kdb->key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1787 if (kdb->key_data->key_data_contents[k] == NULL) {
1788 cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1789 kdb->key_data = NULL;
1790 kdb->n_key_data = 0;
1794 memcpy (kdb->key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1796 memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1797 free (tmp_key_data.key_data_contents[k]);
1798 tmp_key_data.key_data_contents[k] = NULL;
1804 kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1806 ret = krb5_timeofday(handle->context, &now);
1810 if ((adb.aux_attributes & KADM5_POLICY)) {
1811 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1818 * The spec says this check is overridden if the caller has
1819 * modify privilege. The admin server therefore makes this
1820 * check itself (in chpass_principal_wrapper, misc.c). A
1821 * local caller implicitly has all authorization bits.
1823 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1826 if((now - last_pwd) < pol.pw_min_life &&
1827 !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1828 ret = KADM5_PASS_TOOSOON;
1833 if (pol.pw_max_life)
1834 kdb->pw_expiration = now + pol.pw_max_life;
1836 kdb->pw_expiration = 0;
1838 kdb->pw_expiration = 0;
1841 ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1845 /* unlock principal on this KDC */
1846 kdb->fail_auth_count = 0;
1848 if ((ret = kdb_put_entry(handle, kdb, &adb)))
1853 for (i = 0; i < tmp_key_data.key_data_ver; i++) {
1854 if (tmp_key_data.key_data_contents[i]) {
1855 memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
1856 free (tmp_key_data.key_data_contents[i]);
1860 kdb_free_entry(handle, kdb, &adb);
1862 kadm5_free_policy_ent(handle->lhandle, &pol);
1868 kadm5_setkey_principal(void *server_handle,
1869 krb5_principal principal,
1870 krb5_keyblock *keyblocks,
1874 kadm5_setkey_principal_3(server_handle, principal,
1880 kadm5_setkey_principal_3(void *server_handle,
1881 krb5_principal principal,
1882 krb5_boolean keepold,
1883 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1884 krb5_keyblock *keyblocks,
1888 osa_princ_ent_rec adb;
1890 kadm5_policy_ent_rec pol;
1891 krb5_key_data *old_key_data;
1893 int i, j, k, kvno, ret, have_pol = 0;
1897 kadm5_server_handle_t handle = server_handle;
1898 krb5_boolean similar;
1899 krb5_keysalt keysalt;
1900 krb5_key_data tmp_key_data;
1901 krb5_key_data *tptr;
1902 krb5_keyblock *act_mkey;
1904 CHECK_HANDLE(server_handle);
1906 krb5_clear_error_message(handle->context);
1908 if (principal == NULL || keyblocks == NULL)
1910 if (hist_princ && /* this will be NULL when initializing the databse */
1911 ((krb5_principal_compare(handle->context,
1912 principal, hist_princ)) == TRUE))
1913 return KADM5_PROTECT_PRINCIPAL;
1915 for (i = 0; i < n_keys; i++) {
1916 for (j = i+1; j < n_keys; j++) {
1917 if ((ret = krb5_c_enctype_compare(handle->context,
1918 keyblocks[i].enctype,
1919 keyblocks[j].enctype,
1924 if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1925 return KADM5_SETKEY_DUP_ENCTYPES;
1927 return KADM5_SETKEY_DUP_ENCTYPES;
1932 if (n_ks_tuple && n_ks_tuple != n_keys)
1933 return KADM5_SETKEY3_ETYPE_MISMATCH;
1935 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1938 for (kvno = 0, i=0; i<kdb->n_key_data; i++)
1939 if (kdb->key_data[i].key_data_kvno > kvno)
1940 kvno = kdb->key_data[i].key_data_kvno;
1943 old_key_data = kdb->key_data;
1944 n_old_keys = kdb->n_key_data;
1946 if (kdb->key_data != NULL)
1947 cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1949 old_key_data = NULL;
1952 kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
1953 *sizeof(krb5_key_data));
1954 if (kdb->key_data == NULL) {
1959 memset(kdb->key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1960 kdb->n_key_data = 0;
1962 for (i = 0; i < n_keys; i++) {
1964 keysalt.type = ks_tuple[i].ks_salttype;
1965 keysalt.data.length = 0;
1966 keysalt.data.data = NULL;
1967 if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1968 ret = KADM5_SETKEY3_ETYPE_MISMATCH;
1972 memset (&tmp_key_data, 0, sizeof(tmp_key_data));
1974 ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
1975 active_mkey_list, NULL, &act_mkey);
1979 ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
1981 n_ks_tuple ? &keysalt : NULL, kvno + 1,
1986 tptr = &kdb->key_data[i];
1987 tptr->key_data_ver = tmp_key_data.key_data_ver;
1988 tptr->key_data_kvno = tmp_key_data.key_data_kvno;
1989 for (k = 0; k < tmp_key_data.key_data_ver; k++) {
1990 tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
1991 tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
1992 if (tmp_key_data.key_data_contents[k]) {
1993 tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1994 if (tptr->key_data_contents[k] == NULL) {
1996 for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
1997 if (tmp_key_data.key_data_contents[i1]) {
1998 memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
1999 free (tmp_key_data.key_data_contents[i1]);
2006 memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
2008 memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
2009 free (tmp_key_data.key_data_contents[k]);
2010 tmp_key_data.key_data_contents[k] = NULL;
2016 /* copy old key data if necessary */
2017 for (i = 0; i < n_old_keys; i++) {
2018 kdb->key_data[i+n_keys] = old_key_data[i];
2019 memset(&old_key_data[i], 0, sizeof (krb5_key_data));
2024 krb5_db_free(handle->context, old_key_data);
2026 /* assert(kdb->n_key_data == n_keys + n_old_keys) */
2027 kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
2029 if ((ret = krb5_timeofday(handle->context, &now)))
2032 if ((adb.aux_attributes & KADM5_POLICY)) {
2033 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
2040 * The spec says this check is overridden if the caller has
2041 * modify privilege. The admin server therefore makes this
2042 * check itself (in chpass_principal_wrapper, misc.c). A
2043 * local caller implicitly has all authorization bits.
2045 if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
2048 if((now - last_pwd) < pol.pw_min_life &&
2049 !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2050 ret = KADM5_PASS_TOOSOON;
2055 if (pol.pw_max_life)
2056 kdb->pw_expiration = now + pol.pw_max_life;
2058 kdb->pw_expiration = 0;
2060 kdb->pw_expiration = 0;
2063 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
2066 /* unlock principal on this KDC */
2067 kdb->fail_auth_count = 0;
2069 if ((ret = kdb_put_entry(handle, kdb, &adb)))
2074 kdb_free_entry(handle, kdb, &adb);
2076 kadm5_free_policy_ent(handle->lhandle, &pol);
2082 * Return the list of keys like kadm5_randkey_principal,
2083 * but don't modify the principal.
2086 kadm5_get_principal_keys(void *server_handle /* IN */,
2087 krb5_principal principal /* IN */,
2088 krb5_keyblock **keyblocks /* OUT */,
2089 int *n_keys /* OUT */)
2092 osa_princ_ent_rec adb;
2094 kadm5_server_handle_t handle = server_handle;
2099 CHECK_HANDLE(server_handle);
2101 if (principal == NULL)
2104 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
2108 ret = decrypt_key_data(handle->context,
2109 kdb->n_key_data, kdb->key_data,
2117 kdb_free_entry(handle, kdb, &adb);
2124 * Allocate an array of n_key_data krb5_keyblocks, fill in each
2125 * element with the results of decrypting the nth key in key_data,
2126 * and if n_keys is not NULL fill it in with the
2127 * number of keys decrypted.
2129 static int decrypt_key_data(krb5_context context,
2130 int n_key_data, krb5_key_data *key_data,
2131 krb5_keyblock **keyblocks, int *n_keys)
2133 krb5_keyblock *keys;
2136 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
2139 memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
2141 for (i = 0; i < n_key_data; i++) {
2142 ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &keys[i],
2145 for (; i >= 0; i--) {
2146 if (keys[i].contents) {
2147 memset (keys[i].contents, 0, keys[i].length);
2148 free( keys[i].contents );
2152 memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
2160 *n_keys = n_key_data;
2166 * Function: kadm5_decrypt_key
2168 * Purpose: Retrieves and decrypts a principal key.
2172 * server_handle (r) kadm5 handle
2173 * entry (r) principal retrieved with kadm5_get_principal
2174 * ktype (r) enctype to search for, or -1 to ignore
2175 * stype (r) salt type to search for, or -1 to ignore
2176 * kvno (r) kvno to search for, -1 for max, 0 for max
2177 * only if it also matches ktype and stype
2178 * keyblock (w) keyblock to fill in
2179 * keysalt (w) keysalt to fill in, or NULL
2180 * kvnop (w) kvno to fill in, or NULL
2182 * Effects: Searches the key_data array of entry, which must have been
2183 * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2184 * find a key with a specified enctype, salt type, and kvno in a
2185 * principal entry. If not found, return ENOENT. Otherwise, decrypt
2186 * it with the master key, and return the key in keyblock, the salt
2187 * in salttype, and the key version number in kvno.
2189 * If ktype or stype is -1, it is ignored for the search. If kvno is
2190 * -1, ktype and stype are ignored and the key with the max kvno is
2191 * returned. If kvno is 0, only the key with the max kvno is returned
2192 * and only if it matches the ktype and stype; otherwise, ENOENT is
2195 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
2196 kadm5_principal_ent_t entry, krb5_int32
2197 ktype, krb5_int32 stype, krb5_int32
2198 kvno, krb5_keyblock *keyblock,
2199 krb5_keysalt *keysalt, int *kvnop)
2201 kadm5_server_handle_t handle = server_handle;
2202 krb5_db_entry dbent;
2203 krb5_key_data *key_data;
2204 krb5_keyblock *mkey_ptr;
2207 CHECK_HANDLE(server_handle);
2209 if (entry->n_key_data == 0 || entry->key_data == NULL)
2212 /* find_enctype only uses these two fields */
2213 dbent.n_key_data = entry->n_key_data;
2214 dbent.key_data = entry->key_data;
2215 if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
2216 stype, kvno, &key_data)))
2219 /* find_mkey only uses this field */
2220 dbent.tl_data = entry->tl_data;
2221 if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent,
2223 krb5_keylist_node *tmp_mkey_list;
2224 /* try refreshing master key list */
2225 /* XXX it would nice if we had the mkvno here for optimization */
2226 if (krb5_db_fetch_mkey_list(handle->context, master_princ,
2227 &master_keyblock, 0, &tmp_mkey_list) == 0) {
2228 krb5_dbe_free_key_list(handle->context, master_keylist);
2229 master_keylist = tmp_mkey_list;
2230 if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
2231 &dbent, &mkey_ptr))) {
2239 if ((ret = krb5_dbe_decrypt_key_data(handle->context, NULL, key_data,
2240 keyblock, keysalt)))
2244 * Coerce the enctype of the output keyblock in case we got an
2245 * inexact match on the enctype; this behavior will go away when
2246 * the key storage architecture gets redesigned for 1.3.
2249 keyblock->enctype = ktype;
2252 *kvnop = key_data->key_data_kvno;
2258 kadm5_purgekeys(void *server_handle,
2259 krb5_principal principal,
2262 kadm5_server_handle_t handle = server_handle;
2265 osa_princ_ent_rec adb;
2266 krb5_key_data *old_keydata;
2270 CHECK_HANDLE(server_handle);
2272 if (principal == NULL)
2275 ret = kdb_get_entry(handle, principal, &kdb, &adb);
2279 if (keepkvno <= 0) {
2280 keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
2284 old_keydata = kdb->key_data;
2285 n_old_keydata = kdb->n_key_data;
2286 kdb->n_key_data = 0;
2287 kdb->key_data = krb5_db_alloc(handle->context, NULL,
2288 n_old_keydata * sizeof(krb5_key_data));
2289 if (kdb->key_data == NULL) {
2293 memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
2294 for (i = 0, j = 0; i < n_old_keydata; i++) {
2295 if (old_keydata[i].key_data_kvno < keepkvno)
2298 /* Alias the key_data_contents pointers; we null them out in the
2299 * source array immediately after. */
2300 kdb->key_data[j] = old_keydata[i];
2301 for (k = 0; k < old_keydata[i].key_data_ver; k++) {
2302 old_keydata[i].key_data_contents[k] = NULL;
2306 kdb->n_key_data = j;
2307 cleanup_key_data(handle->context, n_old_keydata, old_keydata);
2309 kdb->mask = KADM5_KEY_DATA;
2310 ret = kdb_put_entry(handle, kdb, &adb);
2315 kdb_free_entry(handle, kdb, &adb);