Description: <short summary of the patch>
 TODO: Put a short summary on the line above and replace this paragraph
 with a longer explanation of this change. Complete the meta-information
 with other relevant fields (see below for details). To make it easier, the
 information below has been extracted from the changelog. Adjust it or drop
 it.
 .
 heimdal (7.7.1+dfsg-1) oldstable-heimdal-dev; urgency=medium
 .
   * Backport to stretch.
Author: Adam Henry Lewenberg <adamhl@stanford.edu>

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: https://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: 2019-12-18

--- heimdal-7.7.1+dfsg.orig/kadmin/server.c
+++ heimdal-7.7.1+dfsg/kadmin/server.c
@@ -181,6 +181,21 @@ kadmind_dispatch(void *kadm_handlep, krb
 	krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
 	ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD,
 					  ent.principal);
+
+        if (ret == 0) {
+	    krb5_data pwd_data;
+	    const char *pwd_reason;
+
+	    pwd_data.data = password;
+	    pwd_data.length = strlen(password);
+
+	    pwd_reason = kadm5_check_password_quality (contextp->context,
+						       ent.principal,
+						       &pwd_data);
+	    if (pwd_reason != NULL)
+		ret = KADM5_PASS_Q_DICT;
+        }
+
 	if(ret){
 	    kadm5_free_principal_ent(kadm_handlep, &ent);
 	    goto fail;
@@ -318,9 +333,10 @@ kadmind_dispatch(void *kadm_handlep, krb
 	 *
 	 * a) allowed by sysadmin
 	 * b) it's for the principal him/herself and this was an
-	 *    initial ticket, but then, check with the password quality
-	 *    function.
+	 *    initial ticket
 	 * c) the user is on the CPW ACL.
+	 *
+	 * All changes are checked for password quality.
 	 */
 
 	if (krb5_config_get_bool_default(contextp->context, NULL, TRUE,
@@ -329,6 +345,12 @@ kadmind_dispatch(void *kadm_handlep, krb
 	    && krb5_principal_compare (contextp->context, contextp->caller,
 				       princ))
 	{
+	    ret = 0;
+	} else {
+	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+	}
+
+	if (ret == 0) {
 	    krb5_data pwd_data;
 	    const char *pwd_reason;
 
@@ -339,10 +361,7 @@ kadmind_dispatch(void *kadm_handlep, krb
 						       princ, &pwd_data);
 	    if (pwd_reason != NULL)
 		ret = KADM5_PASS_Q_DICT;
-	    else
-		ret = 0;
-	} else
-	    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+        }
 
 	if(ret) {
 	    krb5_free_principal(contextp->context, princ);
--- heimdal-7.7.1+dfsg.orig/kpasswd/kpasswdd.c
+++ heimdal-7.7.1+dfsg/kpasswd/kpasswdd.c
@@ -366,24 +366,8 @@ change (krb5_auth_context auth_context,
 	goto out;
     }
 
-    /*
-     * Check password quality if not changing as administrator
-     */
-
-    if (krb5_principal_compare(context, admin_principal, principal) == TRUE) {
-
-	pwd_reason = kadm5_check_password_quality (context, principal,
-						   pwd_data);
-	if (pwd_reason != NULL ) {
-	    krb5_warnx (context,
-			"%s didn't pass password quality check with error: %s",
-			client, pwd_reason);
-	    reply_priv (auth_context, s, sa, sa_size,
-			KRB5_KPASSWD_SOFTERROR, pwd_reason);
-	    goto out;
-	}
-	krb5_warnx (context, "Changing password for %s", client);
-    } else {
+    /* Check the ACL if this is an administrator password change. */
+    if (krb5_principal_compare(context, admin_principal, principal) != TRUE) {
 	ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW,
 					  principal);
 	if (ret) {
@@ -394,9 +378,30 @@ change (krb5_auth_context auth_context,
 			KRB5_KPASSWD_HARDERROR, "permission denied");
 	    goto out;
 	}
-	krb5_warnx (context, "%s is changing password for %s", admin, client);
     }
 
+    /*
+     * Check password quality.  This is done regardless of whether the user is
+     * an administrator since the password quality hook is also how password
+     * history is managed, and administrator password changes should go into
+     * history.
+     */
+    pwd_reason = kadm5_check_password_quality (context, principal, pwd_data);
+    if (pwd_reason != NULL ) {
+	krb5_warnx (context,
+		    "%s didn't pass password quality check with error: %s",
+		    client, pwd_reason);
+	reply_priv (auth_context, s, sa, sa_size,
+		    KRB5_KPASSWD_SOFTERROR, pwd_reason);
+	goto out;
+    }
+
+    /* Log the change. */
+    if (krb5_principal_compare(context, admin_principal, principal) == TRUE)
+	krb5_warnx (context, "Changing password for %s", client);
+    else
+	krb5_warnx (context, "%s is changing password for %s", admin, client);
+
     ret = krb5_data_realloc(pwd_data, pwd_data->length + 1);
     if (ret) {
 	krb5_warn (context, ret, "malloc: out of memory");
