You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
91 lines
3.8 KiB
91 lines
3.8 KiB
From ad8e02485791023dcf66ef4612616f03895ceeb3 Mon Sep 17 00:00:00 2001 |
|
From: Greg Hudson <ghudson@mit.edu> |
|
Date: Fri, 4 Mar 2022 00:45:00 -0500 |
|
Subject: [PATCH] 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 9f968da7f..c59232586 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 a985f2aec..91d2791ce 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 0eedec175..c7f5d861a 100644 |
|
--- a/src/lib/krb5/os/sendto_kdc.c |
|
+++ b/src/lib/krb5/os/sendto_kdc.c |
|
@@ -802,11 +802,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); |
|
@@ -850,8 +853,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); |
|
-- |
|
2.35.1 |
|
|
|
|