Blame SOURCES/krb5-krad-remote.patch

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