Blame SOURCES/krb5-krad-remote.patch

73a275
From da677b071dadda3700d12d037f5896b166d3546d Mon Sep 17 00:00:00 2001
73a275
From: Greg Hudson <ghudson@mit.edu>
73a275
Date: Tue, 9 Nov 2021 13:00:43 -0500
73a275
Subject: [PATCH] Avoid use after free during libkrad cleanup
73a275
73a275
libkrad client requests contain a list of references to remotes, with
73a275
no back-references or reference counts.  To prevent accesses to
73a275
dangling references during cleanup, cancel all requests on all remotes
73a275
before freeing any remotes.
73a275
73a275
Remove the code for aging out unused servers.  This code was fairly
73a275
safe as all requests referencing a remote should have completed or
73a275
timed out during an hour of disuse, but in the current design we have
73a275
no way to guarantee or check that.  The set of addresses we send
73a275
RADIUS requests to will generally be small, so aging out servers is
73a275
unnecessary.
73a275
73a275
ticket: 9035 (new)
73a275
---
73a275
 src/lib/krad/client.c   | 42 ++++++++++++++---------------------------
73a275
 src/lib/krad/internal.h |  4 ++++
73a275
 src/lib/krad/remote.c   | 11 ++++++++---
73a275
 3 files changed, 26 insertions(+), 31 deletions(-)
73a275
73a275
diff --git a/src/lib/krad/client.c b/src/lib/krad/client.c
73a275
index 6365dd1c6..810940afc 100644
73a275
--- a/src/lib/krad/client.c
73a275
+++ b/src/lib/krad/client.c
73a275
@@ -64,7 +64,6 @@ struct request_st {
73a275
 
73a275
 struct server_st {
73a275
     krad_remote *serv;
73a275
-    time_t last;
73a275
     K5_LIST_ENTRY(server_st) list;
73a275
 };
73a275
 
73a275
@@ -81,15 +80,10 @@ get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
73a275
            krad_remote **out)
73a275
 {
73a275
     krb5_error_code retval;
73a275
-    time_t currtime;
73a275
     server *srv;
73a275
 
73a275
-    if (time(&currtime) == (time_t)-1)
73a275
-        return errno;
73a275
-
73a275
     K5_LIST_FOREACH(srv, &rc->servers, list) {
73a275
         if (kr_remote_equals(srv->serv, ai, secret)) {
73a275
-            srv->last = currtime;
73a275
             *out = srv->serv;
73a275
             return 0;
73a275
         }
73a275
@@ -98,7 +92,6 @@ get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
73a275
     srv = calloc(1, sizeof(server));
73a275
     if (srv == NULL)
73a275
         return ENOMEM;
73a275
-    srv->last = currtime;
73a275
 
73a275
     retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
73a275
     if (retval != 0) {
73a275
@@ -173,28 +166,12 @@ request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
73a275
     return 0;
73a275
 }
73a275
 
73a275
-/* Close remotes that haven't been used in a while. */
73a275
-static void
73a275
-age(struct server_head *head, time_t currtime)
73a275
-{
73a275
-    server *srv, *tmp;
73a275
-
73a275
-    K5_LIST_FOREACH_SAFE(srv, head, list, tmp) {
73a275
-        if (currtime == (time_t)-1 || currtime - srv->last > 60 * 60) {
73a275
-            K5_LIST_REMOVE(srv, list);
73a275
-            kr_remote_free(srv->serv);
73a275
-            free(srv);
73a275
-        }
73a275
-    }
73a275
-}
73a275
-
73a275
 /* Handle a response from a server (or related errors). */
73a275
 static void
73a275
 on_response(krb5_error_code retval, const krad_packet *reqp,
73a275
             const krad_packet *rspp, void *data)
73a275
 {
73a275
     request *req = data;
73a275
-    time_t currtime;
73a275
     size_t i;
73a275
 
73a275
     /* Do nothing if we are already completed. */
73a275
@@ -221,10 +198,6 @@ on_response(krb5_error_code retval, const krad_packet *reqp,
73a275
     for (i = 0; req->remotes[i].remote != NULL; i++)
73a275
         kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);
73a275
 
73a275
-    /* Age out servers that haven't been used in a while. */
73a275
-    if (time(&currtime) != (time_t)-1)
73a275
-        age(&req->rc->servers, currtime);
73a275
-
73a275
     request_free(req);
73a275
 }
73a275
 
73a275
@@ -247,10 +220,23 @@ krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)
73a275
 void
73a275
 krad_client_free(krad_client *rc)
73a275
 {
73a275
+    server *srv;
73a275
+
73a275
     if (rc == NULL)
73a275
         return;
73a275
 
73a275
-    age(&rc->servers, -1);
73a275
+    /* Cancel all requests before freeing any remotes, since each request's
73a275
+     * callback data may contain references to multiple remotes. */
73a275
+    K5_LIST_FOREACH(srv, &rc->servers, list)
73a275
+        kr_remote_cancel_all(srv->serv);
73a275
+
73a275
+    while (!K5_LIST_EMPTY(&rc->servers)) {
73a275
+        srv = K5_LIST_FIRST(&rc->servers);
73a275
+        K5_LIST_REMOVE(srv, list);
73a275
+        kr_remote_free(srv->serv);
73a275
+        free(srv);
73a275
+    }
73a275
+
73a275
     free(rc);
73a275
 }
73a275
 
73a275
diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
73a275
index 312dc8258..b086598fb 100644
73a275
--- a/src/lib/krad/internal.h
73a275
+++ b/src/lib/krad/internal.h
73a275
@@ -120,6 +120,10 @@ kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
73a275
 void
73a275
 kr_remote_cancel(krad_remote *rr, const krad_packet *pkt);
73a275
 
73a275
+/* Cancel all requests awaiting responses. */
73a275
+void
73a275
+kr_remote_cancel_all(krad_remote *rr);
73a275
+
73a275
 /* Determine if this remote object refers to the remote resource identified
73a275
  * by the addrinfo struct and the secret. */
73a275
 krb5_boolean
73a275
diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
73a275
index 0f90443ce..b5dd8cd19 100644
73a275
--- a/src/lib/krad/remote.c
73a275
+++ b/src/lib/krad/remote.c
73a275
@@ -421,15 +421,20 @@ error:
73a275
     return retval;
73a275
 }
73a275
 
73a275
+void
73a275
+kr_remote_cancel_all(krad_remote *rr)
73a275
+{
73a275
+    while (!K5_TAILQ_EMPTY(&rr->list))
73a275
+        request_finish(K5_TAILQ_FIRST(&rr->list), ECANCELED, NULL);
73a275
+}
73a275
+
73a275
 void
73a275
 kr_remote_free(krad_remote *rr)
73a275
 {
73a275
     if (rr == NULL)
73a275
         return;
73a275
 
73a275
-    while (!K5_TAILQ_EMPTY(&rr->list))
73a275
-        request_finish(K5_TAILQ_FIRST(&rr->list), ECANCELED, NULL);
73a275
-
73a275
+    kr_remote_cancel_all(rr);
73a275
     free(rr->secret);
73a275
     if (rr->info != NULL)
73a275
         free(rr->info->ai_addr);
73a275
-- 
73a275
2.35.3
73a275