krb5 commit: Add request_timeout configuration parameter

ghudson at mit.edu ghudson at mit.edu
Mon Oct 30 22:02:20 EDT 2023


https://github.com/krb5/krb5/commit/802318cda963456b3ed7856c836e89da891483be
commit 802318cda963456b3ed7856c836e89da891483be
Author: Greg Hudson <ghudson at mit.edu>
Date:   Thu Oct 26 14:20:34 2023 -0400

    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)

 doc/admin/conf_files/krb5_conf.rst |  9 +++++++
 src/include/k5-int.h               |  2 ++
 src/lib/krb5/krb/init_ctx.c        | 14 ++++++++++-
 src/lib/krb5/os/sendto_kdc.c       | 51 +++++++++++++++++++++++++-------------
 4 files changed, 58 insertions(+), 18 deletions(-)

diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 651e0e78d..fb9c8b7ab 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 ce74b5acc..fe9959389 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 a6c2bbeb5..ab5c680a2 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 0f198c398..d1254d28e 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;


More information about the cvs-krb5 mailing list