kadmind patch (was: kadm5_randkey_principal_3)

Ben Cox cox at spinnakernet.com
Tue Oct 1 15:54:01 EDT 2002


On the kerberos at mit.edu mailing list, which is admittedly the wrong
list, I wrote:

---8<---snip---8<---
I have a server application that wants to periodically randomize its
service key.  This key MUST be a des-cbc-crc key, and it MUST be the
only current key for the service principal (for reasons that I don't
want to discuss right now; suffice it to say that my application won't
interoperate with its clients if the KDC is going around handing out
service tickets with other key types for my service).  (I know that
single-DES isn't necessarily the tightest cipher in the implementation,
but it's really not up for discussion.)

I'm trying to do this by using kadm5_init_with_skey to the changepw
service, using my current key, and then kadm5_randkey_principal_3,
passing a key-salt tuple containing only one entry: des-cbc-crc:normal. 
(And then calling kadm5_get_principal to get my new kvno, since
kadm5_randkey_principal_3 doesn't give me back that useful nugget of
information...)

The problem is that kadm5_randkey_principal_3 returns TWO keys: a
des3-cbc-raw key and a des-cbc-crc key.  Since it contains a des-cbc-crc
key in the list, I can paw through it and get that key, but this result
doesn't meet my second requirement: that the des-cbc-crc key be the ONLY
current key for the service principal.

(I'm seeing this behavior running MIT k5 1.2.4, and observed the
following in the 1.2.5 source tree.)
 
I've traced through the code, and it appears that if the calling
principal matches the principal whose key is being changed, the
server-side wrapper calls randkey_principal_wrapper, which ignores the
ks_tuple argument and calls kadm5_randkey_principal instead of
kadm5_randkey_principal_3.  (And, maddeningly enough,
kadm5_randkey_principal calls kadm5_randkey_principal_3 with a fixed set
of ks_tuples.)

Bummer.

Is there a good reason for this?  Is it fixed in 1.2.6?  (Seems to me
that one Q&D fix would be trivial: have a randkey_principal_3_wrapper
that calls kadm5_randkey_principal_3 at the end instead of
kadm5_randkey_principal.  But I can't go around handing out kadmind
patches to sites that want to use my server.)

Is there a workaround that doesn't require my keeping credentials around
for some other principal that has the changepw privilege?  (If the
calling principal is someone else, calling the kadmin service, with the
changepw privilege, it works fine, but I don't want to store a key for
that principal.

Thanks in advance for any suggestions,
---8<---snip---8<---

I have attached a patch against the krb5-1.2.6 source tree which fixes
this using the blunt hammer approach described above.  I hope that this
fix (or one like it) can be included in krb5-1.2.7. :)

Thanks,

-- Ben

-------------- next part --------------
--- src/kadmin/server/misc.c	Mon Jul 22 16:28:55 1996
+++ src/kadmin/server/misc.c.new	Tue Oct  1 15:26:10 2002
@@ -77,6 +77,75 @@
 
 
 /*
+ * Function: chpass_principal_wrapper
+ * 
+ * Purpose: wrapper to kadm5_chpass_principal that checks to see if
+ *	    pw_min_life has been reached. if not it returns an error.
+ *	    otherwise it calls kadm5_chpass_principal
+ *
+ * Arguments:
+ *	principal	(input) krb5_principals whose password we are
+ *				changing
+ *	passoword	(input) passowrd we are going to change to.
+ * 	<return value>	0 on sucsess error code on failure.
+ *
+ * Requires:
+ *	kadm5_init to have been run.
+ * 
+ * Effects:
+ *	calls kadm5_chpass_principal which changes the kdb and the
+ *	the admin db.
+ *
+ */
+kadm5_ret_t
+chpass_principal_wrapper_3(void *server_handle,
+			   krb5_principal principal,
+			   krb5_boolean keepold,
+			   int n_ks_tuple,
+			   krb5_key_salt_tuple *ks_tuple,
+			   char *password)
+{
+    krb5_int32			now;
+    kadm5_ret_t			ret;
+    kadm5_policy_ent_rec	pol;
+    kadm5_principal_ent_rec	princ;
+    kadm5_server_handle_t	handle = server_handle;
+
+    if (ret = krb5_timeofday(handle->context, &now))
+	return ret;
+
+    if((ret = kadm5_get_principal(handle->lhandle, principal,
+				  &princ,
+				  KADM5_PRINCIPAL_NORMAL_MASK)) !=
+       KADM5_OK) 
+	 return ret;
+    if(princ.aux_attributes & KADM5_POLICY) {
+	if((ret=kadm5_get_policy(handle->lhandle,
+				 princ.policy, &pol)) != KADM5_OK) {
+	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+	    return ret;
+	}
+	if((now - princ.last_pwd_change) < pol.pw_min_life &&
+	   !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+	    (void) kadm5_free_policy_ent(handle->lhandle, &pol);
+	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+	    return KADM5_PASS_TOOSOON;
+	}
+	if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) {
+	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+	    return ret;
+        }
+    }
+    if (ret = kadm5_free_principal_ent(handle->lhandle, &princ))
+	 return ret;
+    
+    return kadm5_chpass_principal_3(server_handle, principal,
+				    keepold, n_ks_tuple, ks_tuple,
+				    password);
+}
+
+
+/*
  * Function: randkey_principal_wrapper
  * 
  * Purpose: wrapper to kadm5_randkey_principal which checks the
@@ -136,3 +205,70 @@
 	 return ret;
     return kadm5_randkey_principal(server_handle, principal, keys, n_keys);
 }
+
+
+/*
+ * Function: randkey_principal_wrapper_3
+ * 
+ * Purpose: wrapper to kadm5_randkey_principal which checks the
+	    passwords min. life.
+ *
+ * Arguments:
+ *	principal	    (input) krb5_principal whose password we are
+ *				    changing
+ *	key		    (output) new random key
+ * 	<return value>	    0, error code on error.
+ *
+ * Requires:
+ *	kadm5_init	 needs to be run
+ * 
+ * Effects:
+ *	calls kadm5_randkey_principal
+ *
+ */
+kadm5_ret_t
+randkey_principal_wrapper_3(void *server_handle,
+			    krb5_principal principal,
+			    krb5_boolean keepold,
+			    int n_ks_tuple,
+			    krb5_key_salt_tuple *ks_tuple,
+			    krb5_keyblock **keys, int *n_keys)
+{
+
+    krb5_int32			now;
+    kadm5_ret_t			ret;
+    kadm5_policy_ent_rec	pol;
+    kadm5_principal_ent_rec	princ;
+    kadm5_server_handle_t	handle = server_handle;
+
+    if (ret = krb5_timeofday(handle->context, &now))
+	return ret;
+
+    if((ret = kadm5_get_principal(handle->lhandle,
+				  principal, &princ,
+				  KADM5_PRINCIPAL_NORMAL_MASK)) !=
+       OSA_ADB_OK) 
+	 return ret;
+    if(princ.aux_attributes & KADM5_POLICY) {
+	if((ret=kadm5_get_policy(handle->lhandle,
+				 princ.policy, &pol)) != KADM5_OK) {
+	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+	    return ret;
+	}
+	if((now - princ.last_pwd_change) < pol.pw_min_life &&
+	   !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+	    (void) kadm5_free_policy_ent(handle->lhandle, &pol);
+	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+	    return KADM5_PASS_TOOSOON;
+	}
+	if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) {
+	    (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+	    return ret;
+        }
+    }
+    if (ret = kadm5_free_principal_ent(handle->lhandle, &princ))
+	 return ret;
+    return kadm5_randkey_principal_3(server_handle, principal,
+				     keepold, n_ks_tuple, ks_tuple,
+				     keys, n_keys);
+}
--- src/kadmin/server/server_stubs.c	Mon Aug 12 18:55:58 2002
+++ src/kadmin/server/server_stubs.c.new	Tue Oct  1 15:24:57 2002
@@ -744,8 +744,11 @@
     }
 
     if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) {
-	 ret.code = chpass_principal_wrapper((void *)handle, arg->princ,
-					     arg->pass);
+	 ret.code = chpass_principal_wrapper_3((void *)handle, arg->princ,
+					       arg->keepold,
+					       arg->n_ks_tuple,
+					       arg->ks_tuple,
+					       arg->pass);
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
 	       acl_check(handle->context, rqstp->rq_clntcred,
 			 ACL_CHANGEPW, arg->princ, NULL)) {
@@ -1067,8 +1070,11 @@
     }
 
     if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) {
-	 ret.code = randkey_principal_wrapper((void *)handle,
-					      arg->princ, &k, &nkeys); 
+	 ret.code = randkey_principal_wrapper_3((void *)handle, arg->princ,
+						arg->keepold,
+						arg->n_ks_tuple,
+						arg->ks_tuple,
+						&k, &nkeys);
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
 	       acl_check(handle->context, rqstp->rq_clntcred,
 			 ACL_CHANGEPW, arg->princ, NULL)) {


More information about the krbdev mailing list