Allow clearpolicy restriction for kadmin addprinc
[krb5.git] / src / lib / kadm5 / srv / svr_principal.c
index 696362ac60ed42be499611e30320b511960c3a59..a0b110def012d343d536ad59ded21219348e7c08 100644 (file)
@@ -4,21 +4,14 @@
  *
  * $Header$
  */
-#include <assert.h>
-#include        <sys/types.h>
+#include "k5-int.h"
 #include        <sys/time.h>
-#include        <errno.h>
 #include        <kadm5/admin.h>
 #include        <kdb.h>
-#include        <stdio.h>
-#include        <string.h>
 #include        "server_internal.h"
-#include        <stdarg.h>
-#include        <stdlib.h>
 #ifdef USE_PASSWORD_SERVER
 #include        <sys/wait.h>
 #include        <signal.h>
-
 #endif
 
 #include <krb5/kadm5_hook_plugin.h>
@@ -32,7 +25,6 @@
 extern  krb5_principal      master_princ;
 extern  krb5_principal      hist_princ;
 extern  krb5_keyblock       master_keyblock;
-extern  krb5_keylist_node  *master_keylist;
 extern  krb5_actkvno_node  *active_mkey_list;
 extern  krb5_db_entry       master_db;
 
@@ -244,10 +236,11 @@ kadm5_create_principal_3(void *server_handle,
      */
     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
-       (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
-       (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
-       (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
-       (mask & KADM5_FAIL_AUTH_COUNT))
+       (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
+       (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
+       (mask & KADM5_LAST_FAILED) || (mask & KADM5_FAIL_AUTH_COUNT))
+        return KADM5_BAD_MASK;
+    if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
         return KADM5_BAD_MASK;
     if((mask & ~ALL_PRINC_MASK))
         return KADM5_BAD_MASK;
@@ -371,8 +364,8 @@ kadm5_create_principal_3(void *server_handle,
 
     /* initialize the keys */
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                 active_mkey_list, &act_kvno, &act_mkey);
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno,
+                                 &act_mkey);
     if (ret)
         goto cleanup;
 
@@ -730,10 +723,12 @@ kadm5_ret_t
 kadm5_rename_principal(void *server_handle,
                        krb5_principal source, krb5_principal target)
 {
-    krb5_db_entry       *kdb;
-    osa_princ_ent_rec   adb;
-    int                 ret, i;
+    krb5_db_entry *kdb;
+    osa_princ_ent_rec adb;
+    krb5_error_code ret;
     kadm5_server_handle_t handle = server_handle;
+    krb5_int16 stype, i;
+    krb5_data *salt = NULL;
 
     CHECK_HANDLE(server_handle);
 
@@ -750,14 +745,21 @@ kadm5_rename_principal(void *server_handle,
     if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
         return ret;
 
-    /* this is kinda gross, but unavoidable */
-
-    for (i=0; i<kdb->n_key_data; i++) {
-        if ((kdb->key_data[i].key_data_ver == 1) ||
-            (kdb->key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
+    /* Transform salts as necessary. */
+    for (i = 0; i < kdb->n_key_data; i++) {
+        ret = krb5_dbe_compute_salt(handle->context, &kdb->key_data[i],
+                                    kdb->princ, &stype, &salt);
+        if (ret == KRB5_KDB_BAD_SALTTYPE)
             ret = KADM5_NO_RENAME_SALT;
+        if (ret)
             goto done;
-        }
+        kdb->key_data[i].key_data_type[1] = KRB5_KDB_SALTTYPE_SPECIAL;
+        free(kdb->key_data[i].key_data_contents[1]);
+        kdb->key_data[i].key_data_contents[1] = (krb5_octet *)salt->data;
+        kdb->key_data[i].key_data_length[1] = salt->length;
+        kdb->key_data[i].key_data_ver = 2;
+        free(salt);
+        salt = NULL;
     }
 
     kadm5_free_principal(handle->context, kdb->princ);
@@ -773,6 +775,7 @@ kadm5_rename_principal(void *server_handle,
     ret = kdb_delete_entry(handle, source);
 
 done:
+    krb5_free_data(handle->context, salt);
     kdb_free_entry(handle, kdb, &adb);
     return ret;
 }
@@ -866,8 +869,7 @@ kadm5_get_principal(void *server_handle, krb5_principal principal,
                 entry->kvno = kdb->key_data[i].key_data_kvno;
 
     if (mask & KADM5_MKVNO) {
-        ret = krb5_dbe_get_mkvno(handle->context, kdb, master_keylist,
-                                 &entry->mkvno);
+        ret = krb5_dbe_get_mkvno(handle->context, kdb, &entry->mkvno);
         if (ret)
             goto done;
     }
@@ -961,38 +963,42 @@ done:
  */
 static kadm5_ret_t
 check_pw_reuse(krb5_context context,
-               krb5_keyblock *hist_keyblock,
+               krb5_keyblock *hist_keyblocks,
                int n_new_key_data, krb5_key_data *new_key_data,
                unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
 {
     unsigned int x, y, z;
-    krb5_keyblock newkey, histkey;
+    krb5_keyblock newkey, histkey, *kb;
+    krb5_key_data *key_data;
     krb5_error_code ret;
 
     assert (n_new_key_data >= 0);
     for (x = 0; x < (unsigned) n_new_key_data; x++) {
+        /* Check only entries with the most recent kvno. */
+        if (new_key_data[x].key_data_kvno != new_key_data[0].key_data_kvno)
+            break;
         ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]),
                                         &newkey, NULL);
         if (ret)
             return(ret);
         for (y = 0; y < n_pw_hist_data; y++) {
             for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) {
-                ret = krb5_dbe_decrypt_key_data(context, hist_keyblock,
-                                                &pw_hist_data[y].key_data[z],
-                                                &histkey, NULL);
-                if (ret)
-                    return(ret);
-
-                if ((newkey.length == histkey.length) &&
-                    (newkey.enctype == histkey.enctype) &&
-                    (memcmp(newkey.contents, histkey.contents,
-                            histkey.length) == 0)) {
+                for (kb = hist_keyblocks; kb->enctype != 0; kb++) {
+                    key_data = &pw_hist_data[y].key_data[z];
+                    ret = krb5_dbe_decrypt_key_data(context, kb, key_data,
+                                                    &histkey, NULL);
+                    if (ret)
+                        continue;
+                    if (newkey.length == histkey.length &&
+                        newkey.enctype == histkey.enctype &&
+                        memcmp(newkey.contents, histkey.contents,
+                               histkey.length) == 0) {
+                        krb5_free_keyblock_contents(context, &histkey);
+                        krb5_free_keyblock_contents(context, &newkey);
+                        return KADM5_PASS_REUSE;
+                    }
                     krb5_free_keyblock_contents(context, &histkey);
-                    krb5_free_keyblock_contents(context, &newkey);
-
-                    return(KADM5_PASS_REUSE);
                 }
-                krb5_free_keyblock_contents(context, &histkey);
             }
         }
         krb5_free_keyblock_contents(context, &newkey);
@@ -1332,12 +1338,12 @@ kadm5_chpass_principal_3(void *server_handle,
     krb5_int32                  now;
     kadm5_policy_ent_rec        pol;
     osa_princ_ent_rec           adb;
-    krb5_db_entry               *kdb, *kdb_save;
+    krb5_db_entry               *kdb;
     int                         ret, ret2, last_pwd, hist_added;
     int                         have_pol = 0;
     kadm5_server_handle_t       handle = server_handle;
     osa_pw_hist_ent             hist;
-    krb5_keyblock               *act_mkey, hist_keyblock;
+    krb5_keyblock               *act_mkey, *hist_keyblocks = NULL;
     krb5_kvno                   act_kvno, hist_kvno;
 
     CHECK_HANDLE(server_handle);
@@ -1346,7 +1352,6 @@ kadm5_chpass_principal_3(void *server_handle,
 
     hist_added = 0;
     memset(&hist, 0, sizeof(hist));
-    memset(&hist_keyblock, 0, sizeof(hist_keyblock));
 
     if (principal == NULL || password == NULL)
         return EINVAL;
@@ -1363,24 +1368,27 @@ kadm5_chpass_principal_3(void *server_handle,
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
         return(ret);
 
-    /* we are going to need the current keys after the new keys are set */
-    if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
-        kdb_free_entry(handle, kdb, &adb);
-        return(ret);
-    }
-
     if ((adb.aux_attributes & KADM5_POLICY)) {
         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
             goto done;
         have_pol = 1;
+
+        /* Create a password history entry before we change kdb's key_data. */
+        ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
+        if (ret)
+            goto done;
+        ret = create_history_entry(handle->context, &hist_keyblocks[0],
+                                   kdb->n_key_data, kdb->key_data, &hist);
+        if (ret)
+            goto done;
     }
 
     if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
                             principal)))
         goto done;
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                 active_mkey_list, &act_kvno, &act_mkey);
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno,
+                                 &act_mkey);
     if (ret)
         goto done;
 
@@ -1421,18 +1429,7 @@ kadm5_chpass_principal_3(void *server_handle,
         }
 #endif
 
-        ret = kdb_get_hist_key(handle, &hist_keyblock, &hist_kvno);
-        if (ret)
-            goto done;
-
-        ret = create_history_entry(handle->context,
-                                   &hist_keyblock,
-                                   kdb_save->n_key_data,
-                                   kdb_save->key_data, &hist);
-        if (ret)
-            goto done;
-
-        ret = check_pw_reuse(handle->context, &hist_keyblock,
+        ret = check_pw_reuse(handle->context, hist_keyblocks,
                              kdb->n_key_data, kdb->key_data,
                              1, &hist);
         if (ret)
@@ -1442,7 +1439,7 @@ kadm5_chpass_principal_3(void *server_handle,
             /* If hist_kvno has changed since the last password change, we
              * can't check the history. */
             if (adb.admin_history_kvno == hist_kvno) {
-                ret = check_pw_reuse(handle->context, &hist_keyblock,
+                ret = check_pw_reuse(handle->context, hist_keyblocks,
                                      kdb->n_key_data, kdb->key_data,
                                      adb.old_key_len, adb.old_keys);
                 if (ret)
@@ -1522,8 +1519,7 @@ done:
     if (!hist_added && hist.key_data)
         free_history_entry(handle->context, &hist);
     kdb_free_entry(handle, kdb, &adb);
-    kdb_free_entry(handle, kdb_save, NULL);
-    krb5_free_keyblock_contents(handle->context, &hist_keyblock);
+    kdb_free_keyblocks(handle, hist_keyblocks);
 
     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
         && !ret)
@@ -1585,8 +1581,8 @@ kadm5_randkey_principal_3(void *server_handle,
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
         return(ret);
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                 active_mkey_list, NULL, &act_mkey);
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+                                 &act_mkey);
     if (ret)
         goto done;
 
@@ -1733,8 +1729,8 @@ kadm5_setv4key_principal(void *server_handle,
     keysalt.data.length = 0;
     keysalt.data.data = NULL;
 
-    ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                 active_mkey_list, NULL, &act_mkey);
+    ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+                                 &act_mkey);
     if (ret)
         goto done;
 
@@ -1937,8 +1933,8 @@ kadm5_setkey_principal_3(void *server_handle,
         }
         memset (&tmp_key_data, 0, sizeof(tmp_key_data));
 
-        ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
-                                     active_mkey_list, NULL, &act_mkey);
+        ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
+                                     &act_mkey);
         if (ret)
             goto done;
 
@@ -2184,17 +2180,13 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
 
     /* find_mkey only uses this field */
     dbent.tl_data = entry->tl_data;
-    if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist, &dbent,
-                                  &mkey_ptr))) {
-        krb5_keylist_node *tmp_mkey_list;
+    if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, &mkey_ptr))) {
         /* try refreshing master key list */
         /* XXX it would nice if we had the mkvno here for optimization */
         if (krb5_db_fetch_mkey_list(handle->context, master_princ,
-                                    &master_keyblock, 0, &tmp_mkey_list) == 0) {
-            krb5_dbe_free_key_list(handle->context, master_keylist);
-            master_keylist = tmp_mkey_list;
-            if ((ret = krb5_dbe_find_mkey(handle->context, master_keylist,
-                                          &dbent, &mkey_ptr))) {
+                                    &master_keyblock) == 0) {
+            if ((ret = krb5_dbe_find_mkey(handle->context, &dbent,
+                                          &mkey_ptr))) {
                 return ret;
             }
         } else {
@@ -2281,3 +2273,55 @@ done:
     kdb_free_entry(handle, kdb, &adb);
     return ret;
 }
+
+kadm5_ret_t
+kadm5_get_strings(void *server_handle, krb5_principal principal,
+                  krb5_string_attr **strings_out, int *count_out)
+{
+    kadm5_server_handle_t handle = server_handle;
+    kadm5_ret_t ret;
+    krb5_db_entry *kdb = NULL;
+
+    *strings_out = NULL;
+    *count_out = 0;
+    CHECK_HANDLE(server_handle);
+    if (principal == NULL)
+        return EINVAL;
+
+    ret = kdb_get_entry(handle, principal, &kdb, NULL);
+    if (ret)
+        return ret;
+
+    ret = krb5_dbe_get_strings(handle->context, kdb, strings_out, count_out);
+    kdb_free_entry(handle, kdb, NULL);
+    return ret;
+}
+
+kadm5_ret_t
+kadm5_set_string(void *server_handle, krb5_principal principal,
+                 const char *key, const char *value)
+{
+    kadm5_server_handle_t handle = server_handle;
+    kadm5_ret_t ret;
+    krb5_db_entry *kdb;
+    osa_princ_ent_rec adb;
+
+    CHECK_HANDLE(server_handle);
+    if (principal == NULL || key == NULL)
+        return EINVAL;
+
+    ret = kdb_get_entry(handle, principal, &kdb, &adb);
+    if (ret)
+        return ret;
+
+    ret = krb5_dbe_set_string(handle->context, kdb, key, value);
+    if (ret)
+        goto done;
+
+    kdb->mask = KADM5_TL_DATA;
+    ret = kdb_put_entry(handle, kdb, &adb);
+
+done:
+    kdb_free_entry(handle, kdb, &adb);
+    return ret;
+}