From: Greg Hudson Date: Thu, 26 Oct 2023 18:20:34 +0000 (-0400) Subject: Add request_timeout configuration parameter X-Git-Tag: krb5-1.22-beta1~131 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=802318cda963456b3ed7856c836e89da891483be;p=thirdparty%2Fkrb5.git Add request_timeout configuration parameter Add a parameter to limit the total amount of time taken for a KDC or password change request. ticket: 9106 (new) --- diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst index 651e0e78dd..fb9c8b7ab9 100644 --- a/doc/admin/conf_files/krb5_conf.rst +++ b/doc/admin/conf_files/krb5_conf.rst @@ -362,6 +362,15 @@ The libdefaults section may contain any of the following relations: (:ref:`duration` string.) Sets the default renewable lifetime for initial ticket requests. The default value is 0. +**request_timeout** + (:ref:`duration` string.) Sets the maximum total time for KDC or + password change requests. This timeout does not affect the + intervals between requests, so setting a low timeout may result in + fewer requests being attempted and/or some servers not being + contacted. A value of 0 indicates no specific maximum, in which + case requests will time out if no server responds after several + tries. The default value is 0. (New in release 1.22.) + **spake_preauth_groups** A whitespace or comma-separated list of words which specifies the groups allowed for SPAKE preauthentication. The possible values diff --git a/src/include/k5-int.h b/src/include/k5-int.h index ce74b5acc6..fe99593896 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -297,6 +297,7 @@ typedef unsigned char u_char; #define KRB5_CONF_SPAKE_PREAUTH_INDICATOR "spake_preauth_indicator" #define KRB5_CONF_SPAKE_PREAUTH_KDC_CHALLENGE "spake_preauth_kdc_challenge" #define KRB5_CONF_SPAKE_PREAUTH_GROUPS "spake_preauth_groups" +#define KRB5_CONF_REQUEST_TIMEOUT "request_timeout" #define KRB5_CONF_TICKET_LIFETIME "ticket_lifetime" #define KRB5_CONF_UDP_PREFERENCE_LIMIT "udp_preference_limit" #define KRB5_CONF_UNLOCKITER "unlockiter" @@ -1201,6 +1202,7 @@ struct _krb5_context { kdb5_dal_handle *dal_handle; /* allowable clock skew */ krb5_deltat clockskew; + krb5_deltat req_timeout; krb5_flags kdc_default_options; krb5_flags library_options; krb5_boolean profile_secure; diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c index a6c2bbeb54..ab5c680a2a 100644 --- a/src/lib/krb5/krb/init_ctx.c +++ b/src/lib/krb5/krb/init_ctx.c @@ -158,7 +158,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags, krb5_context ctx = 0; krb5_error_code retval; int tmp; - char *plugin_dir = NULL; + char *plugin_dir = NULL, *timeout_str = NULL; /* Verify some assumptions. If the assumptions hold and the compiler is optimizing, this should result in no code being @@ -251,6 +251,17 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags, get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp); ctx->clockskew = tmp; + retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS, + KRB5_CONF_REQUEST_TIMEOUT, NULL, NULL, + &timeout_str); + if (retval) + goto cleanup; + if (timeout_str != NULL) { + retval = krb5_string_to_deltat(timeout_str, &ctx->req_timeout); + if (retval) + goto cleanup; + } + get_integer(ctx, KRB5_CONF_KDC_DEFAULT_OPTIONS, KDC_OPT_RENEWABLE_OK, &tmp); ctx->kdc_default_options = tmp; @@ -292,6 +303,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags, cleanup: profile_release_string(plugin_dir); + profile_release_string(timeout_str); krb5_free_context(ctx); return retval; } diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c index 0f198c3987..d1254d28e7 100644 --- a/src/lib/krb5/os/sendto_kdc.c +++ b/src/lib/krb5/os/sendto_kdc.c @@ -1397,34 +1397,41 @@ get_endtime(time_ms endtime, struct conn_state *conns) static krb5_boolean service_fds(krb5_context context, struct select_state *selstate, - time_ms interval, struct conn_state *conns, + time_ms interval, time_ms timeout, struct conn_state *conns, struct select_state *seltemp, const krb5_data *realm, int (*msg_handler)(krb5_context, const krb5_data *, void *), void *msg_handler_data, struct conn_state **winner_out) { int e, selret = 0; - time_ms endtime; + time_ms curtime, interval_end, endtime; struct conn_state *state; *winner_out = NULL; - e = get_curtime_ms(&endtime); + e = get_curtime_ms(&curtime); if (e) return TRUE; - endtime += interval; + interval_end = curtime + interval; e = 0; while (selstate->nfds > 0) { - e = cm_select_or_poll(selstate, get_endtime(endtime, conns), - seltemp, &selret); + endtime = get_endtime(interval_end, conns); + /* Don't wait longer than the whole request should last. */ + if (timeout && endtime > timeout) + endtime = timeout; + e = cm_select_or_poll(selstate, endtime, seltemp, &selret); if (e == EINTR) continue; if (e != 0) break; - if (selret == 0) - /* Timeout, return to caller. */ + if (selret == 0) { + /* We timed out. Stop if we hit the overall request timeout. */ + if (timeout && (get_curtime_ms(&curtime) || curtime >= timeout)) + return TRUE; + /* Otherwise return to the caller to send the next request. */ return FALSE; + } /* Got something on a socket, process it. */ for (state = conns; state != NULL; state = state->next) { @@ -1497,7 +1504,7 @@ k5_sendto(krb5_context context, const krb5_data *message, void *msg_handler_data) { int pass; - time_ms delay; + time_ms delay, timeout = 0; krb5_error_code retval; struct conn_state *conns = NULL, *state, **tailptr, *next, *winner; size_t s; @@ -1507,6 +1514,13 @@ k5_sendto(krb5_context context, const krb5_data *message, *reply = empty_data(); + if (context->req_timeout) { + retval = get_curtime_ms(&timeout); + if (retval) + return retval; + timeout += 1000 * context->req_timeout; + } + /* One for use here, listing all our fds in use, and one for * temporary use in service_fds, for the fds of interest. */ sel_state = malloc(2 * sizeof(*sel_state)); @@ -1534,8 +1548,9 @@ k5_sendto(krb5_context context, const krb5_data *message, if (maybe_send(context, state, message, sel_state, realm, callback_info)) continue; - done = service_fds(context, sel_state, 1000, conns, seltemp, - realm, msg_handler, msg_handler_data, &winner); + done = service_fds(context, sel_state, 1000, timeout, conns, + seltemp, realm, msg_handler, msg_handler_data, + &winner); } } @@ -1547,13 +1562,13 @@ k5_sendto(krb5_context context, const krb5_data *message, if (maybe_send(context, state, message, sel_state, realm, callback_info)) continue; - done = service_fds(context, sel_state, 1000, conns, seltemp, + done = service_fds(context, sel_state, 1000, timeout, conns, seltemp, realm, msg_handler, msg_handler_data, &winner); } /* Wait for two seconds at the end of the first pass. */ if (!done) { - done = service_fds(context, sel_state, 2000, conns, seltemp, + done = service_fds(context, sel_state, 2000, timeout, conns, seltemp, realm, msg_handler, msg_handler_data, &winner); } @@ -1564,15 +1579,17 @@ k5_sendto(krb5_context context, const krb5_data *message, if (maybe_send(context, state, message, sel_state, realm, callback_info)) continue; - done = service_fds(context, sel_state, 1000, conns, seltemp, - realm, msg_handler, msg_handler_data, &winner); + done = service_fds(context, sel_state, 1000, timeout, conns, + seltemp, realm, msg_handler, msg_handler_data, + &winner); if (sel_state->nfds == 0) break; } /* Wait for the delay backoff at the end of this pass. */ if (!done) { - done = service_fds(context, sel_state, delay, conns, seltemp, - realm, msg_handler, msg_handler_data, &winner); + done = service_fds(context, sel_state, delay, timeout, conns, + seltemp, realm, msg_handler, msg_handler_data, + &winner); } if (sel_state->nfds == 0) break;