Blame SOURCES/krb5-krad-remote.patch

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