Browse Source

ubus: add ACL support for "subscribe" request

With this change ubus will allow users with access to the object pseudo
method ":subscribe" to subscribe for notifications.

1. Move uh_ubus_allowed() up in the code
2. Export "Authorization" parsing code to the uh_ubus_get_auth()
3. Check for ":subscribe" method access

Right now this depends on "Authorization" HTTP header which browsers
don't allow setting for the EventSource. An alternative method of
submitting session token remains to be implemented.

Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
Rafał Miłecki 3 years ago
parent
commit
47c34bd6ad
1 changed files with 64 additions and 55 deletions
  1. 64 55
      ubus.c

+ 64 - 55
ubus.c

@@ -112,6 +112,30 @@ enum cors_hdr {
 	__HDR_MAX
 };
 
+enum ubus_hdr {
+	HDR_AUTHORIZATION,
+	__HDR_UBUS_MAX
+};
+
+static const char *uh_ubus_get_auth(const struct blob_attr *attr)
+{
+	static const struct blobmsg_policy hdr_policy[__HDR_UBUS_MAX] = {
+		[HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING },
+	};
+	struct blob_attr *tb[__HDR_UBUS_MAX];
+
+	blobmsg_parse(hdr_policy, __HDR_UBUS_MAX, tb, blob_data(attr), blob_len(attr));
+
+	if (tb[HDR_AUTHORIZATION]) {
+		const char *tmp = blobmsg_get_string(tb[HDR_AUTHORIZATION]);
+
+		if (!strncasecmp(tmp, "Bearer ", 7))
+			return tmp + 7;
+	}
+
+	return UH_UBUS_DEFAULT_SID;
+}
+
 static void __uh_ubus_next_batched_request(struct uloop_timeout *timeout);
 
 static void uh_ubus_next_batched_request(struct client *cl)
@@ -239,6 +263,39 @@ static void uh_ubus_ubus_error(struct client *cl, int err)
 	uh_ubus_error(cl, err, ubus_strerror(err));
 }
 
+static void uh_ubus_allowed_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+	struct blob_attr *tb[__SES_MAX];
+	bool *allow = (bool *)req->priv;
+
+	if (!msg)
+		return;
+
+	blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (tb[SES_ACCESS])
+		*allow = blobmsg_get_bool(tb[SES_ACCESS]);
+}
+
+static bool uh_ubus_allowed(const char *sid, const char *obj, const char *fun)
+{
+	uint32_t id;
+	bool allow = false;
+	static struct blob_buf req;
+
+	if (ubus_lookup_id(ctx, "session", &id))
+		return false;
+
+	blob_buf_init(&req, 0);
+	blobmsg_add_string(&req, "ubus_rpc_session", sid);
+	blobmsg_add_string(&req, "object", obj);
+	blobmsg_add_string(&req, "function", fun);
+
+	ubus_invoke(ctx, id, "access", req.head, uh_ubus_allowed_cb, &allow, conf.script_timeout * 500);
+
+	return allow;
+}
+
 /* GET requests handling */
 
 static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv);
@@ -303,14 +360,16 @@ static void uh_ubus_subscription_notification_remove_cb(struct ubus_context *ctx
 	ops->request_done(cl);
 }
 
-static void uh_ubus_handle_get_subscribe(struct client *cl, const char *sid, const char *path)
+static void uh_ubus_handle_get_subscribe(struct client *cl, const char *path)
 {
 	struct dispatch_ubus *du = &cl->dispatch.ubus;
+	const char *sid;
 	uint32_t id;
 	int err;
 
-	/* TODO: add ACL support */
-	if (!conf.ubus_noauth) {
+	sid = uh_ubus_get_auth(cl->hdr.head);
+
+	if (!conf.ubus_noauth && !uh_ubus_allowed(sid, path, ":subscribe")) {
 		uh_ubus_send_header(cl, 200, "OK", "application/json");
 		uh_ubus_posix_error(cl, EACCES);
 		return;
@@ -364,7 +423,7 @@ static void uh_ubus_handle_get(struct client *cl)
 	} else if (!strncmp(url, "/subscribe/", strlen("/subscribe/"))) {
 		url += strlen("/subscribe");
 
-		uh_ubus_handle_get_subscribe(cl, NULL, url + 1);
+		uh_ubus_handle_get_subscribe(cl, url + 1);
 	} else {
 		ops->http_header(cl, 404, "Not Found");
 		ustream_printf(cl->us, "\r\n");
@@ -682,39 +741,6 @@ static void uh_ubus_complete_batch(struct client *cl)
 	ops->request_done(cl);
 }
 
-static void uh_ubus_allowed_cb(struct ubus_request *req, int type, struct blob_attr *msg)
-{
-	struct blob_attr *tb[__SES_MAX];
-	bool *allow = (bool *)req->priv;
-
-	if (!msg)
-		return;
-
-	blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg));
-
-	if (tb[SES_ACCESS])
-		*allow = blobmsg_get_bool(tb[SES_ACCESS]);
-}
-
-static bool uh_ubus_allowed(const char *sid, const char *obj, const char *fun)
-{
-	uint32_t id;
-	bool allow = false;
-	static struct blob_buf req;
-
-	if (ubus_lookup_id(ctx, "session", &id))
-		return false;
-
-	blob_buf_init(&req, 0);
-	blobmsg_add_string(&req, "ubus_rpc_session", sid);
-	blobmsg_add_string(&req, "object", obj);
-	blobmsg_add_string(&req, "function", fun);
-
-	ubus_invoke(ctx, id, "access", req.head, uh_ubus_allowed_cb, &allow, conf.script_timeout * 500);
-
-	return allow;
-}
-
 static void uh_ubus_handle_request_object(struct client *cl, struct json_object *obj)
 {
 	struct dispatch_ubus *du = &cl->dispatch.ubus;
@@ -851,18 +877,9 @@ out:
 	uh_client_unref(cl);
 }
 
-enum ubus_hdr {
-	HDR_AUTHORIZATION,
-	__HDR_UBUS_MAX
-};
-
 static void uh_ubus_handle_post(struct client *cl)
 {
-	static const struct blobmsg_policy hdr_policy[__HDR_UBUS_MAX] = {
-		[HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING },
-	};
 	struct dispatch_ubus *du = &cl->dispatch.ubus;
-	struct blob_attr *tb[__HDR_UBUS_MAX];
 	const char *url = du->url_path;
 	const char *auth;
 
@@ -873,15 +890,7 @@ static void uh_ubus_handle_post(struct client *cl)
 		return;
 	}
 
-	blobmsg_parse(hdr_policy, __HDR_UBUS_MAX, tb, blob_data(cl->hdr.head), blob_len(cl->hdr.head));
-
-	auth = UH_UBUS_DEFAULT_SID;
-	if (tb[HDR_AUTHORIZATION]) {
-		const char *tmp = blobmsg_get_string(tb[HDR_AUTHORIZATION]);
-
-		if (!strncasecmp(tmp, "Bearer ", 7))
-			auth = tmp + 7;
-	}
+	auth = uh_ubus_get_auth(cl->hdr.head);
 
 	url += strlen(conf.ubus_prefix);