+ ret = decrypt_key_data(handle->context,
+ kdb->n_key_data, kdb->key_data,
+ keyblocks, n_keys);
+ if (ret)
+ goto done;
+ }
+
+ /* key data changed, let the database provider know */
+ kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT;
+ /* | KADM5_RANDKEY_USED */;
+
+ ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
+ n_ks_tuple, ks_tuple, NULL);
+ if (ret)
+ goto done;
+ if ((ret = kdb_put_entry(handle, kdb, &adb)))
+ goto done;
+
+ (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_POSTCOMMIT, principal,
+ keepold, n_ks_tuple, ks_tuple, NULL);
+ ret = KADM5_OK;
+done:
+ kdb_free_entry(handle, kdb, &adb);
+ if (have_pol)
+ kadm5_free_policy_ent(handle->lhandle, &pol);
+
+ return ret;
+}
+
+/*
+ * kadm5_setv4key_principal:
+ *
+ * Set only ONE key of the principal, removing all others. This key
+ * must have the DES_CBC_CRC enctype and is entered as having the
+ * krb4 salttype. This is to enable things like kadmind4 to work.
+ */
+kadm5_ret_t
+kadm5_setv4key_principal(void *server_handle,
+ krb5_principal principal,
+ krb5_keyblock *keyblock)
+{
+ krb5_db_entry *kdb;
+ osa_princ_ent_rec adb;
+ krb5_int32 now;
+ kadm5_policy_ent_rec pol;
+ krb5_keysalt keysalt;
+ int i, k, kvno, ret, have_pol = 0;
+#if 0
+ int last_pwd;
+#endif
+ kadm5_server_handle_t handle = server_handle;
+ krb5_key_data tmp_key_data;
+ krb5_keyblock *act_mkey;
+
+ memset( &tmp_key_data, 0, sizeof(tmp_key_data));
+
+ CHECK_HANDLE(server_handle);
+
+ krb5_clear_error_message(handle->context);
+
+ if (principal == NULL || keyblock == NULL)
+ return EINVAL;
+ if (hist_princ && /* this will be NULL when initializing the databse */
+ ((krb5_principal_compare(handle->context,
+ principal, hist_princ)) == TRUE))
+ return KADM5_PROTECT_PRINCIPAL;
+
+ if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
+ return KADM5_SETV4KEY_INVAL_ENCTYPE;
+
+ if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+ return(ret);
+
+ for (kvno = 0, i=0; i<kdb->n_key_data; i++)
+ if (kdb->key_data[i].key_data_kvno > kvno)
+ kvno = kdb->key_data[i].key_data_kvno;
+
+ if (kdb->key_data != NULL)
+ cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
+
+ kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
+ if (kdb->key_data == NULL)
+ return ENOMEM;
+ memset(kdb->key_data, 0, sizeof(krb5_key_data));
+ kdb->n_key_data = 1;
+ keysalt.type = KRB5_KDB_SALTTYPE_V4;
+ /* XXX data.magic? */
+ keysalt.data.length = 0;
+ keysalt.data.data = NULL;
+
+ ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+ &act_mkey);
+ if (ret)
+ goto done;
+
+ /* use tmp_key_data as temporary location and reallocate later */
+ ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, keyblock,
+ &keysalt, kvno + 1, &tmp_key_data);
+ if (ret) {
+ goto done;
+ }
+
+ for (k = 0; k < tmp_key_data.key_data_ver; k++) {
+ kdb->key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
+ kdb->key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
+ if (tmp_key_data.key_data_contents[k]) {
+ kdb->key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
+ if (kdb->key_data->key_data_contents[k] == NULL) {
+ cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
+ kdb->key_data = NULL;
+ kdb->n_key_data = 0;
+ ret = ENOMEM;
+ goto done;
+ }
+ memcpy (kdb->key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
+
+ memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
+ free (tmp_key_data.key_data_contents[k]);
+ tmp_key_data.key_data_contents[k] = NULL;
+ }
+ }
+
+
+
+ kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+
+ ret = krb5_timeofday(handle->context, &now);
+ if (ret)
+ goto done;
+
+ if ((adb.aux_attributes & KADM5_POLICY)) {
+ if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
+ &pol)) != KADM5_OK)
+ goto done;
+ have_pol = 1;
+
+#if 0
+ /*
+ * The spec says this check is overridden if the caller has
+ * modify privilege. The admin server therefore makes this
+ * check itself (in chpass_principal_wrapper, misc.c). A
+ * local caller implicitly has all authorization bits.
+ */
+ if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
+ kdb, &last_pwd))
+ goto done;
+ if((now - last_pwd) < pol.pw_min_life &&
+ !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ ret = KADM5_PASS_TOOSOON;
+ goto done;
+ }
+#endif
+
+ if (pol.pw_max_life)
+ kdb->pw_expiration = now + pol.pw_max_life;
+ else
+ kdb->pw_expiration = 0;
+ } else {
+ kdb->pw_expiration = 0;
+ }
+
+ ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
+ if (ret)
+ goto done;
+
+ /* unlock principal on this KDC */
+ kdb->fail_auth_count = 0;
+
+ if ((ret = kdb_put_entry(handle, kdb, &adb)))
+ goto done;
+
+ ret = KADM5_OK;
+done:
+ for (i = 0; i < tmp_key_data.key_data_ver; i++) {
+ if (tmp_key_data.key_data_contents[i]) {
+ memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
+ free (tmp_key_data.key_data_contents[i]);
+ }
+ }
+
+ kdb_free_entry(handle, kdb, &adb);
+ if (have_pol)
+ kadm5_free_policy_ent(handle->lhandle, &pol);
+
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_setkey_principal(void *server_handle,
+ krb5_principal principal,
+ krb5_keyblock *keyblocks,
+ int n_keys)
+{
+ return
+ kadm5_setkey_principal_3(server_handle, principal,
+ FALSE, 0, NULL,
+ keyblocks, n_keys);
+}
+
+kadm5_ret_t
+kadm5_setkey_principal_3(void *server_handle,
+ krb5_principal principal,
+ krb5_boolean keepold,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock *keyblocks,
+ int n_keys)
+{
+ krb5_db_entry *kdb;
+ osa_princ_ent_rec adb;
+ krb5_int32 now;
+ kadm5_policy_ent_rec pol;
+ krb5_key_data *old_key_data;
+ int n_old_keys;
+ int i, j, k, kvno, ret, have_pol = 0;
+#if 0
+ int last_pwd;
+#endif
+ kadm5_server_handle_t handle = server_handle;
+ krb5_boolean similar;
+ krb5_keysalt keysalt;
+ krb5_key_data tmp_key_data;
+ krb5_key_data *tptr;
+ krb5_keyblock *act_mkey;
+
+ CHECK_HANDLE(server_handle);
+
+ krb5_clear_error_message(handle->context);
+
+ if (principal == NULL || keyblocks == NULL)
+ return EINVAL;
+ if (hist_princ && /* this will be NULL when initializing the databse */
+ ((krb5_principal_compare(handle->context,
+ principal, hist_princ)) == TRUE))
+ return KADM5_PROTECT_PRINCIPAL;
+
+ for (i = 0; i < n_keys; i++) {
+ for (j = i+1; j < n_keys; j++) {
+ if ((ret = krb5_c_enctype_compare(handle->context,
+ keyblocks[i].enctype,
+ keyblocks[j].enctype,
+ &similar)))
+ return(ret);
+ if (similar) {
+ if (n_ks_tuple) {
+ if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
+ return KADM5_SETKEY_DUP_ENCTYPES;
+ } else
+ return KADM5_SETKEY_DUP_ENCTYPES;
+ }
+ }
+ }
+
+ if (n_ks_tuple && n_ks_tuple != n_keys)
+ return KADM5_SETKEY3_ETYPE_MISMATCH;
+
+ if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+ return(ret);
+
+ for (kvno = 0, i=0; i<kdb->n_key_data; i++)
+ if (kdb->key_data[i].key_data_kvno > kvno)
+ kvno = kdb->key_data[i].key_data_kvno;
+
+ if (keepold) {
+ old_key_data = kdb->key_data;
+ n_old_keys = kdb->n_key_data;
+ } else {
+ if (kdb->key_data != NULL)
+ cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
+ n_old_keys = 0;
+ old_key_data = NULL;
+ }
+
+ kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
+ *sizeof(krb5_key_data));
+ if (kdb->key_data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ memset(kdb->key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
+ kdb->n_key_data = 0;
+
+ for (i = 0; i < n_keys; i++) {
+ if (n_ks_tuple) {
+ keysalt.type = ks_tuple[i].ks_salttype;
+ keysalt.data.length = 0;
+ keysalt.data.data = NULL;
+ if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
+ ret = KADM5_SETKEY3_ETYPE_MISMATCH;
+ goto done;
+ }
+ }
+ memset (&tmp_key_data, 0, sizeof(tmp_key_data));
+
+ ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+ &act_mkey);
+ if (ret)
+ goto done;
+
+ ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
+ &keyblocks[i],
+ n_ks_tuple ? &keysalt : NULL, kvno + 1,
+ &tmp_key_data);
+ if (ret)
+ goto done;
+
+ tptr = &kdb->key_data[i];
+ tptr->key_data_ver = tmp_key_data.key_data_ver;
+ tptr->key_data_kvno = tmp_key_data.key_data_kvno;
+ for (k = 0; k < tmp_key_data.key_data_ver; k++) {
+ tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
+ tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
+ if (tmp_key_data.key_data_contents[k]) {
+ tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
+ if (tptr->key_data_contents[k] == NULL) {
+ int i1;
+ for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
+ if (tmp_key_data.key_data_contents[i1]) {
+ memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
+ free (tmp_key_data.key_data_contents[i1]);
+ }
+ }
+
+ ret = ENOMEM;
+ goto done;
+ }
+ memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
+
+ memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
+ free (tmp_key_data.key_data_contents[k]);
+ tmp_key_data.key_data_contents[k] = NULL;
+ }
+ }
+ kdb->n_key_data++;
+ }
+
+ /* copy old key data if necessary */
+ for (i = 0; i < n_old_keys; i++) {
+ kdb->key_data[i+n_keys] = old_key_data[i];
+ memset(&old_key_data[i], 0, sizeof (krb5_key_data));
+ kdb->n_key_data++;
+ }
+
+ if (old_key_data)
+ krb5_db_free(handle->context, old_key_data);
+
+ /* assert(kdb->n_key_data == n_keys + n_old_keys) */
+ kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+
+ if ((ret = krb5_timeofday(handle->context, &now)))
+ goto done;
+
+ if ((adb.aux_attributes & KADM5_POLICY)) {
+ if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
+ &pol)) != KADM5_OK)
+ goto done;
+ have_pol = 1;
+
+#if 0
+ /*
+ * The spec says this check is overridden if the caller has
+ * modify privilege. The admin server therefore makes this
+ * check itself (in chpass_principal_wrapper, misc.c). A
+ * local caller implicitly has all authorization bits.
+ */
+ if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
+ kdb, &last_pwd))
+ goto done;
+ if((now - last_pwd) < pol.pw_min_life &&
+ !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ ret = KADM5_PASS_TOOSOON;
+ goto done;
+ }
+#endif
+
+ if (pol.pw_max_life)
+ kdb->pw_expiration = now + pol.pw_max_life;
+ else
+ kdb->pw_expiration = 0;
+ } else {
+ kdb->pw_expiration = 0;
+ }
+
+ if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
+ goto done;
+
+ /* unlock principal on this KDC */
+ kdb->fail_auth_count = 0;
+
+ if ((ret = kdb_put_entry(handle, kdb, &adb)))
+ goto done;