diff options
author | Greg Hudson <ghudson@mit.edu> | 2022-03-04 00:45:00 -0500 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2022-03-17 13:34:40 -0400 |
commit | 6297788e24cefa8f3fdd36f514e2e6569fa7b34a (patch) | |
tree | 4ed0a39b7e459c24c7dd70e8ad5fc0228f092219 | |
parent | cbfe46ce20f3e9265baa9c648390148c739ab830 (diff) | |
download | krb5-6297788e24cefa8f3fdd36f514e2e6569fa7b34a.zip krb5-6297788e24cefa8f3fdd36f514e2e6569fa7b34a.tar.gz krb5-6297788e24cefa8f3fdd36f514e2e6569fa7b34a.tar.bz2 |
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
-rw-r--r-- | src/lib/krb5/os/changepw.c | 9 | ||||
-rw-r--r-- | src/lib/krb5/os/os-proto.h | 1 | ||||
-rw-r--r-- | 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); |