]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Avoid use after free during libkrad cleanup
authorGreg Hudson <ghudson@mit.edu>
Tue, 9 Nov 2021 18:00:43 +0000 (13:00 -0500)
committerGreg Hudson <ghudson@mit.edu>
Wed, 10 Nov 2021 15:59:29 +0000 (10:59 -0500)
libkrad client requests contain a list of references to remotes, with
no back-references or reference counts.  To prevent accesses to
dangling references during cleanup, cancel all requests on all remotes
before freeing any remotes.

Remove the code for aging out unused servers.  This code was fairly
safe as all requests referencing a remote should have completed or
timed out during an hour of disuse, but in the current design we have
no way to guarantee or check that.  The set of addresses we send
RADIUS requests to will generally be small, so aging out servers is
unnecessary.

ticket: 9035 (new)

src/lib/krad/client.c
src/lib/krad/internal.h
src/lib/krad/remote.c

index 6365dd1c659eaee2db2f7a2c2f6e979bf666b129..810940afc8d4078d82f4a737ac9fbf6486225239 100644 (file)
@@ -64,7 +64,6 @@ struct request_st {
 
 struct server_st {
     krad_remote *serv;
-    time_t last;
     K5_LIST_ENTRY(server_st) list;
 };
 
@@ -81,15 +80,10 @@ get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
            krad_remote **out)
 {
     krb5_error_code retval;
-    time_t currtime;
     server *srv;
 
-    if (time(&currtime) == (time_t)-1)
-        return errno;
-
     K5_LIST_FOREACH(srv, &rc->servers, list) {
         if (kr_remote_equals(srv->serv, ai, secret)) {
-            srv->last = currtime;
             *out = srv->serv;
             return 0;
         }
@@ -98,7 +92,6 @@ get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
     srv = calloc(1, sizeof(server));
     if (srv == NULL)
         return ENOMEM;
-    srv->last = currtime;
 
     retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
     if (retval != 0) {
@@ -173,28 +166,12 @@ request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
     return 0;
 }
 
-/* Close remotes that haven't been used in a while. */
-static void
-age(struct server_head *head, time_t currtime)
-{
-    server *srv, *tmp;
-
-    K5_LIST_FOREACH_SAFE(srv, head, list, tmp) {
-        if (currtime == (time_t)-1 || currtime - srv->last > 60 * 60) {
-            K5_LIST_REMOVE(srv, list);
-            kr_remote_free(srv->serv);
-            free(srv);
-        }
-    }
-}
-
 /* Handle a response from a server (or related errors). */
 static void
 on_response(krb5_error_code retval, const krad_packet *reqp,
             const krad_packet *rspp, void *data)
 {
     request *req = data;
-    time_t currtime;
     size_t i;
 
     /* Do nothing if we are already completed. */
@@ -221,10 +198,6 @@ on_response(krb5_error_code retval, const krad_packet *reqp,
     for (i = 0; req->remotes[i].remote != NULL; i++)
         kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);
 
-    /* Age out servers that haven't been used in a while. */
-    if (time(&currtime) != (time_t)-1)
-        age(&req->rc->servers, currtime);
-
     request_free(req);
 }
 
@@ -247,10 +220,23 @@ krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)
 void
 krad_client_free(krad_client *rc)
 {
+    server *srv;
+
     if (rc == NULL)
         return;
 
-    age(&rc->servers, -1);
+    /* Cancel all requests before freeing any remotes, since each request's
+     * callback data may contain references to multiple remotes. */
+    K5_LIST_FOREACH(srv, &rc->servers, list)
+        kr_remote_cancel_all(srv->serv);
+
+    while (!K5_LIST_EMPTY(&rc->servers)) {
+        srv = K5_LIST_FIRST(&rc->servers);
+        K5_LIST_REMOVE(srv, list);
+        kr_remote_free(srv->serv);
+        free(srv);
+    }
+
     free(rc);
 }
 
index 0143d155a10009a7723fb38780e11b97cb76dadd..7619563fc56c1446763cab818de379824cb6a488 100644 (file)
@@ -109,6 +109,10 @@ kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
 void
 kr_remote_cancel(krad_remote *rr, const krad_packet *pkt);
 
+/* Cancel all requests awaiting responses. */
+void
+kr_remote_cancel_all(krad_remote *rr);
+
 /* Determine if this remote object refers to the remote resource identified
  * by the addrinfo struct and the secret. */
 krb5_boolean
index 7e491e994622cf944cbccc91eabc780c89880f3e..06ae751bc877cfe72e07acc5ce2ddca92414acb4 100644 (file)
@@ -421,15 +421,20 @@ error:
     return retval;
 }
 
+void
+kr_remote_cancel_all(krad_remote *rr)
+{
+    while (!K5_TAILQ_EMPTY(&rr->list))
+        request_finish(K5_TAILQ_FIRST(&rr->list), ECANCELED, NULL);
+}
+
 void
 kr_remote_free(krad_remote *rr)
 {
     if (rr == NULL)
         return;
 
-    while (!K5_TAILQ_EMPTY(&rr->list))
-        request_finish(K5_TAILQ_FIRST(&rr->list), ECANCELED, NULL);
-
+    kr_remote_cancel_all(rr);
     free(rr->secret);
     if (rr->info != NULL)
         free(rr->info->ai_addr);