krb5 commit: Try harder to avoid password change replay errors

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/6297788e24cefa8f3fdd36f514e2e6569fa7b34a
commit 6297788e24cefa8f3fdd36f514e2e6569fa7b34a
Author: Greg Hudson <[email protected]>
Date:   Fri Mar 4 00:45:00 2022 -0500

    Try harder to avoid password change replay errors
    
    Commit d7b3018d338fc9c989c3fa17505870f23c3759a8 (ticket 7905) changed
    change_set_password() to prefer TCP.  However, because UDP_LAST falls
    back to UDP after one second, we can still get a replay error due to a
    dropped packet, before the TCP layer has a chance to retry.
    
    Instead, try k5_sendto() with NO_UDP, and only fall back to UDP after
    TCP fails completely without reaching a server.  In sendto_kdc.c,
    implement an ONLY_UDP transport strategy to allow the UDP fallback.
    
    ticket: 9037

 src/lib/krb5/os/changepw.c   |    9 ++++++++-
 src/lib/krb5/os/os-proto.h   |    1 +
 src/lib/krb5/os/sendto_kdc.c |   12 ++++++++----
 3 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
index 9f968da..c592325 100644
--- a/src/lib/krb5/os/changepw.c
+++ b/src/lib/krb5/os/changepw.c
@@ -255,9 +255,16 @@ change_set_password(krb5_context context,
     callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
     krb5_free_data_contents(callback_ctx.context, &chpw_rep);
 
+    /* UDP retransmits may be seen as replays.  Only try UDP after other
+     * transports fail completely. */
     code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
-                     &sl, UDP_LAST, &callback_info, &chpw_rep,
+                     &sl, NO_UDP, &callback_info, &chpw_rep,
                      ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
+    if (code == KRB5_KDC_UNREACH) {
+        code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
+                         &sl, ONLY_UDP, &callback_info, &chpw_rep,
+                         ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
+    }
     if (code)
         goto cleanup;
 
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
index a985f2a..91d2791 100644
--- a/src/lib/krb5/os/os-proto.h
+++ b/src/lib/krb5/os/os-proto.h
@@ -49,6 +49,7 @@ typedef enum {
     UDP_FIRST = 0,
     UDP_LAST,
     NO_UDP,
+    ONLY_UDP
 } k5_transport_strategy;
 
 /* A single server hostname or address. */
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 8620ffa..0f4bf23 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -804,11 +804,14 @@ resolve_server(krb5_context context, const krb5_data *realm,
     int err, result;
     char portbuf[PORT_LENGTH];
 
-    /* Skip UDP entries if we don't want UDP. */
+    /* Skip entries excluded by the strategy. */
     if (strategy == NO_UDP && entry->transport == UDP)
         return 0;
+    if (strategy == ONLY_UDP && entry->transport != UDP &&
+        entry->transport != TCP_OR_UDP)
+        return 0;
 
-    transport = (strategy == UDP_FIRST) ? UDP : TCP;
+    transport = (strategy == UDP_FIRST || strategy == ONLY_UDP) ? UDP : TCP;
     if (entry->hostname == NULL) {
         /* Added by a module, so transport is either TCP or UDP. */
         ai.ai_socktype = socktype_for_transport(entry->transport);
@@ -852,8 +855,9 @@ resolve_server(krb5_context context, const krb5_data *realm,
     }
 
     /* For TCP_OR_UDP entries, add each address again with the non-preferred
-     * transport, unless we are avoiding UDP.  Flag these as deferred. */
-    if (retval == 0 && entry->transport == TCP_OR_UDP && strategy != NO_UDP) {
+     * transport, if there is one.  Flag these as deferred. */
+    if (retval == 0 && entry->transport == TCP_OR_UDP &&
+        (strategy == UDP_FIRST || strategy == UDP_LAST)) {
         transport = (strategy == UDP_FIRST) ? TCP : UDP;
         for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
             a->ai_socktype = socktype_for_transport(transport);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.