From b8691136be8bf4b3a8e81132f377d1de8b05958a Mon Sep 17 00:00:00 2001 From: Michael Pfeifroth Date: Mon, 10 Aug 2026 16:03:21 +0200 Subject: [PATCH] ubus: honour "X-Ubus-No-Touch: 1" HTTP header on /ubus/ Add optional client-side signalling to tell rpcd that the current /ubus/ request is background activity (LuCI's periodic Poll refreshes and status page reloads) that must not refresh the session idle timer. Together with the matching rpcd change (openwrt/rpcd#39), this lets sessiontime work as documented for LuCI. Parse "X-Ubus-No-Touch: 1" from the HTTP request headers alongside the existing "Authorization" header, then forward the hint as the new "notouch" boolean argument to session/access in two places: * uhttpd's internal ACL check (uh_ubus_allowed), which runs before every downstream RPC dispatched over /ubus/; * a direct session/access RPC invoked by the client itself (LuCI uses this to probe access-group ACLs from its own view code). Only session/access is intercepted; other object/method pairs pass through unmodified. Header absent -> zero behavioural change. Signed-off-by: Michael Pfeifroth --- ubus.c | 31 +++++++++++++++++++++++++++---- uhttpd.h | 1 + 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ubus.c b/ubus.c index 24f4bf4..1b31a71 100644 --- a/ubus.c +++ b/ubus.c @@ -136,6 +136,22 @@ static const char *uh_ubus_get_auth(const struct blob_attr *attr) return UH_UBUS_DEFAULT_SID; } +static bool uh_ubus_get_notouch(const struct blob_attr *attr) +{ + enum { HDR_NOTOUCH, __HDR_NOTOUCH_MAX }; + static const struct blobmsg_policy hdr_policy[__HDR_NOTOUCH_MAX] = { + [HDR_NOTOUCH] = { "x-ubus-no-touch", BLOBMSG_TYPE_STRING }, + }; + struct blob_attr *tb[__HDR_NOTOUCH_MAX]; + + blobmsg_parse(hdr_policy, __HDR_NOTOUCH_MAX, tb, blob_data(attr), blob_len(attr)); + + if (!tb[HDR_NOTOUCH]) + return false; + + return !strcmp(blobmsg_get_string(tb[HDR_NOTOUCH]), "1"); +} + static void __uh_ubus_next_batched_request(struct uloop_timeout *timeout); static void uh_ubus_next_batched_request(struct client *cl) @@ -277,7 +293,7 @@ static void uh_ubus_allowed_cb(struct ubus_request *req, int type, struct blob_a *allow = blobmsg_get_bool(tb[SES_ACCESS]); } -static bool uh_ubus_allowed(const char *sid, const char *obj, const char *fun) +static bool uh_ubus_allowed(struct client *cl, const char *sid, const char *obj, const char *fun) { uint32_t id; bool allow = false; @@ -290,6 +306,8 @@ static bool uh_ubus_allowed(const char *sid, const char *obj, const char *fun) blobmsg_add_string(&req, "ubus_rpc_session", sid); blobmsg_add_string(&req, "object", obj); blobmsg_add_string(&req, "function", fun); + if (cl && cl->dispatch.ubus.notouch) + blobmsg_add_u8(&req, "notouch", 1); ubus_invoke(ctx, id, "access", req.head, uh_ubus_allowed_cb, &allow, conf.script_timeout * 500); @@ -379,7 +397,7 @@ static void uh_ubus_handle_get_subscribe(struct client *cl, const char *path) sid = uh_ubus_get_auth(cl->hdr.head); - if (!conf.ubus_noauth && !uh_ubus_allowed(sid, path, ":subscribe")) { + if (!conf.ubus_noauth && !uh_ubus_allowed(cl, sid, path, ":subscribe")) { uh_ubus_send_header(cl, 200, "OK", "application/json"); uh_ubus_posix_error(cl, EACCES); return; @@ -583,6 +601,9 @@ static void uh_ubus_send_request(struct client *cl, const char *sid, struct blob blobmsg_add_string(&req, "ubus_rpc_session", sid); + if (du->notouch) + blobmsg_add_u8(&req, "notouch", 1); + blob_buf_init(&du->buf, 0); memset(&du->req, 0, sizeof(du->req)); ret = ubus_invoke_async(ctx, du->obj, du->func, req.head, &du->req); @@ -800,7 +821,7 @@ static void uh_ubus_handle_request_object(struct client *cl, struct json_object goto error; } - if (!conf.ubus_noauth && !uh_ubus_allowed(data.sid, data.object, data.function)) { + if (!conf.ubus_noauth && !uh_ubus_allowed(cl, data.sid, data.object, data.function)) { err = ERROR_ACCESS; goto error; } @@ -887,7 +908,7 @@ static void uh_ubus_call(struct client *cl, const char *path, const char *sid) goto error; } - if (!conf.ubus_noauth && !uh_ubus_allowed(sid, path, data.method)) { + if (!conf.ubus_noauth && !uh_ubus_allowed(cl, sid, path, data.method)) { err = ERROR_ACCESS; goto error; } @@ -972,6 +993,8 @@ static void uh_ubus_handle_request(struct client *cl, char *url, struct path_inf if (chr) chr[0] = '\0'; + du->notouch = uh_ubus_get_notouch(cl->hdr.head); + du->legacy = false; d->free = uh_ubus_request_free; diff --git a/uhttpd.h b/uhttpd.h index 9a9fc3c..3b5ccdc 100644 --- a/uhttpd.h +++ b/uhttpd.h @@ -258,6 +258,7 @@ struct dispatch_ubus { bool array; int array_idx; bool legacy; /* Got legacy request => use legacy reply */ + bool notouch; /* Request carried "X-Ubus-No-Touch: 1": pass notouch=1 to session/access */ struct ubus_subscriber sub; };