Blame SOURCES/Try-harder-to-avoid-password-change-replay-errors.patch

8d1257
From 6b4cdaac48e6b736b66ccc21f4eed7c6fc4c2e4a Mon Sep 17 00:00:00 2001
8d1257
From: Greg Hudson <ghudson@mit.edu>
8d1257
Date: Fri, 4 Mar 2022 00:45:00 -0500
8d1257
Subject: [PATCH] Try harder to avoid password change replay errors
8d1257
8d1257
Commit d7b3018d338fc9c989c3fa17505870f23c3759a8 (ticket 7905) changed
8d1257
change_set_password() to prefer TCP.  However, because UDP_LAST falls
8d1257
back to UDP after one second, we can still get a replay error due to a
8d1257
dropped packet, before the TCP layer has a chance to retry.
8d1257
8d1257
Instead, try k5_sendto() with NO_UDP, and only fall back to UDP after
8d1257
TCP fails completely without reaching a server.  In sendto_kdc.c,
8d1257
implement an ONLY_UDP transport strategy to allow the UDP fallback.
8d1257
8d1257
ticket: 9037
8d1257
---
8d1257
 src/lib/krb5/os/changepw.c   |  9 ++++++++-
8d1257
 src/lib/krb5/os/os-proto.h   |  1 +
8d1257
 src/lib/krb5/os/sendto_kdc.c | 12 ++++++++----
8d1257
 3 files changed, 17 insertions(+), 5 deletions(-)
8d1257
8d1257
diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
8d1257
index 9f968da7f..c59232586 100644
8d1257
--- a/src/lib/krb5/os/changepw.c
8d1257
+++ b/src/lib/krb5/os/changepw.c
8d1257
@@ -255,9 +255,16 @@ change_set_password(krb5_context context,
8d1257
     callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
8d1257
     krb5_free_data_contents(callback_ctx.context, &chpw_rep);
8d1257
 
8d1257
+    /* UDP retransmits may be seen as replays.  Only try UDP after other
8d1257
+     * transports fail completely. */
8d1257
     code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
8d1257
-                     &sl, UDP_LAST, &callback_info, &chpw_rep,
8d1257
+                     &sl, NO_UDP, &callback_info, &chpw_rep,
8d1257
                      ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
8d1257
+    if (code == KRB5_KDC_UNREACH) {
8d1257
+        code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
8d1257
+                         &sl, ONLY_UDP, &callback_info, &chpw_rep,
8d1257
+                         ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
8d1257
+    }
8d1257
     if (code)
8d1257
         goto cleanup;
8d1257
 
8d1257
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
8d1257
index a16a34b74..ad3839131 100644
8d1257
--- a/src/lib/krb5/os/os-proto.h
8d1257
+++ b/src/lib/krb5/os/os-proto.h
8d1257
@@ -49,6 +49,7 @@ typedef enum {
8d1257
     UDP_FIRST = 0,
8d1257
     UDP_LAST,
8d1257
     NO_UDP,
8d1257
+    ONLY_UDP
8d1257
 } k5_transport_strategy;
8d1257
 
8d1257
 /* A single server hostname or address. */
8d1257
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
8d1257
index 82523c561..d76e24ccf 100644
8d1257
--- a/src/lib/krb5/os/sendto_kdc.c
8d1257
+++ b/src/lib/krb5/os/sendto_kdc.c
8d1257
@@ -799,11 +799,14 @@ resolve_server(krb5_context context, const krb5_data *realm,
8d1257
     int err, result;
8d1257
     char portbuf[PORT_LENGTH];
8d1257
 
8d1257
-    /* Skip UDP entries if we don't want UDP. */
8d1257
+    /* Skip entries excluded by the strategy. */
8d1257
     if (strategy == NO_UDP && entry->transport == UDP)
8d1257
         return 0;
8d1257
+    if (strategy == ONLY_UDP && entry->transport != UDP &&
8d1257
+        entry->transport != TCP_OR_UDP)
8d1257
+        return 0;
8d1257
 
8d1257
-    transport = (strategy == UDP_FIRST) ? UDP : TCP;
8d1257
+    transport = (strategy == UDP_FIRST || strategy == ONLY_UDP) ? UDP : TCP;
8d1257
     if (entry->hostname == NULL) {
8d1257
         /* Added by a module, so transport is either TCP or UDP. */
8d1257
         ai.ai_socktype = socktype_for_transport(entry->transport);
8d1257
@@ -847,8 +850,9 @@ resolve_server(krb5_context context, const krb5_data *realm,
8d1257
     }
8d1257
 
8d1257
     /* For TCP_OR_UDP entries, add each address again with the non-preferred
8d1257
-     * transport, unless we are avoiding UDP.  Flag these as deferred. */
8d1257
-    if (retval == 0 && entry->transport == TCP_OR_UDP && strategy != NO_UDP) {
8d1257
+     * transport, if there is one.  Flag these as deferred. */
8d1257
+    if (retval == 0 && entry->transport == TCP_OR_UDP &&
8d1257
+        (strategy == UDP_FIRST || strategy == UDP_LAST)) {
8d1257
         transport = (strategy == UDP_FIRST) ? TCP : UDP;
8d1257
         for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
8d1257
             a->ai_socktype = socktype_for_transport(transport);
8d1257
-- 
8d1257
2.35.1
8d1257