[pulseaudio-discuss] [PATCH 4/5] protocol-native: add access checks

Wim Taymans wim.taymans at gmail.com
Tue Apr 7 08:13:16 PDT 2015


Call the hooks in various places to check if an action is allowed or
not.

The hook can return PA_HOOK_OK to allow an action, PA_HOOK_STOP to
deny an action.

Returning PA_HOOK_CANCEL will handle the result asynchronously.
This is implemented by saving the state of the current command and
exiting the command (going back to the mainloop) without reply.
When the access check completes, it will call async_finish_cb, which
will use the saved state to call the original command again. The access
check will eventually return OK or STOP and the command can complete
and send a reply.
---
 src/pulsecore/protocol-native.c | 219 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 217 insertions(+), 2 deletions(-)

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index b06f553..f23de31 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -294,6 +294,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
 static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static pa_hook_result_t check_access(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata, uint32_t idx, pa_subscription_event_type_t event, const char *name);
 
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_ERROR] = NULL,
@@ -1972,6 +1973,23 @@ if (!(expression)) { \
 } \
 } while(0);
 
+#define CHECK_ACCESS_STMT(c, command, tag, idx, name, async, denied) { \
+  pa_hook_result_t res = check_access(pd, command, tag, t, userdata, idx, 0, name); \
+  if (res == PA_HOOK_STOP) { \
+      denied; \
+  } else if (res == PA_HOOK_CANCEL) { \
+      async; \
+  } \
+};
+
+#define CHECK_ACCESS_GOTO(c, command, tag, idx, name, label) \
+    CHECK_ACCESS_STMT(c, command, tag, idx, name, \
+        goto label, pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS); goto label)
+
+#define CHECK_ACCESS(c, command, tag, idx, name) \
+    CHECK_ACCESS_STMT(c, command, tag, idx, name, \
+        return, pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS); return)
+
 static pa_tagstruct *reply_new(uint32_t tag) {
     pa_tagstruct *reply;
 
@@ -2049,6 +2067,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     CHECK_VALIDITY_GOTO(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
     CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
 
+    CHECK_ACCESS_GOTO(c, command, tag, PA_INVALID_INDEX, NULL, finish);
+
     p = pa_proplist_new();
 
     if (name)
@@ -2370,6 +2390,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     CHECK_VALIDITY_GOTO(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID, finish);
     CHECK_VALIDITY_GOTO(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
 
+    CHECK_ACCESS_GOTO(c, command, tag, PA_INVALID_INDEX, NULL, finish);
+
     p = pa_proplist_new();
 
     if (name)
@@ -2575,6 +2597,7 @@ static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
     }
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_ACCESS(c, command, tag, PA_INVALID_INDEX, NULL);
     ret = pa_core_exit(c->protocol->core, false, 0);
     CHECK_VALIDITY(c->pstream, ret >= 0, tag, PA_ERR_ACCESS);
 
@@ -2893,6 +2916,7 @@ static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
     }
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_ACCESS(c, command, tag, PA_INVALID_INDEX, NULL);
 
     stat = pa_mempool_get_stat(c->protocol->core->mempool);
 
@@ -3019,6 +3043,8 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin
     CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
     CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
 
+    CHECK_ACCESS(c, command, tag, PA_INVALID_INDEX, NULL);
+
     p = pa_proplist_new();
 
     if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
@@ -3117,6 +3143,8 @@ static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag
 
     CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
+    CHECK_ACCESS(c, command, tag, sink->index, name);
+
     p = pa_proplist_new();
 
     if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
@@ -3160,6 +3188,8 @@ static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t t
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
     CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
 
+    CHECK_ACCESS(c, command, tag, PA_INVALID_INDEX, name);
+
     if (pa_scache_remove_item(c->protocol->core, name) < 0) {
         pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
         return;
@@ -3608,6 +3638,8 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         return;
     }
 
+    CHECK_ACCESS(c, command, tag, idx, name);
+
     reply = reply_new(tag);
     if (sink)
         sink_fill_tagstruct(c, reply, sink);
@@ -3668,6 +3700,8 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
 
     if (i) {
         PA_IDXSET_FOREACH(p, i, idx) {
+            CHECK_ACCESS_STMT(c, command, tag, idx, NULL, goto async, continue);
+
             if (command == PA_COMMAND_GET_SINK_INFO_LIST)
                 sink_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
@@ -3690,6 +3724,11 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
+    return;
+
+async:
+    pa_tagstruct_free(reply);
+    return;
 }
 
 static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -3710,6 +3749,15 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
 
+    CHECK_ACCESS(c, command, tag, PA_INVALID_INDEX, NULL);
+
+    def_sink = pa_namereg_get_default_sink(c->protocol->core);
+    if (def_sink)
+        CHECK_ACCESS_STMT(c, PA_COMMAND_GET_SINK_INFO, tag, def_sink->index, NULL, return, def_sink = NULL);
+    def_source = pa_namereg_get_default_source(c->protocol->core);
+    if (def_source)
+        CHECK_ACCESS_STMT(c, PA_COMMAND_GET_SOURCE_INFO, tag, def_source->index, NULL, return, def_source = NULL);
+
     reply = reply_new(tag);
     pa_tagstruct_puts(reply, PACKAGE_NAME);
     pa_tagstruct_puts(reply, PACKAGE_VERSION);
@@ -3725,9 +3773,7 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
     fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
     pa_tagstruct_put_sample_spec(reply, &fixed_ss);
 
-    def_sink = pa_namereg_get_default_sink(c->protocol->core);
     pa_tagstruct_puts(reply, def_sink ? def_sink->name : NULL);
-    def_source = pa_namereg_get_default_source(c->protocol->core);
     pa_tagstruct_puts(reply, def_source ? def_source->name : NULL);
 
     pa_tagstruct_putu32(reply, c->protocol->core->cookie);
@@ -3744,6 +3790,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3
 
     pa_native_connection_assert_ref(c);
 
+    if (check_access (NULL, PA_COMMAND_SUBSCRIBE_EVENT, 0, NULL, userdata, idx, e, NULL) != PA_HOOK_OK)
+      return;
+
     t = pa_tagstruct_new();
     pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
     pa_tagstruct_putu32(t, (uint32_t) -1);
@@ -3846,6 +3895,8 @@ static void command_set_volume(
 
     client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
 
+    CHECK_ACCESS(c, command, tag, idx, name);
+
     if (sink) {
         CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &sink->sample_spec), tag, PA_ERR_INVALID);
 
@@ -3941,6 +3992,8 @@ static void command_set_mute(
 
     CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
 
+    CHECK_ACCESS(c, command, tag, idx, name);
+
     client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
 
     if (sink) {
@@ -4404,6 +4457,7 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman
 
         source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE);
         CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+        CHECK_ACCESS(c, command, tag, source->index, s);
 
         pa_namereg_set_default_source(c->protocol->core, source);
     } else {
@@ -4412,6 +4466,7 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman
 
         sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
         CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+        CHECK_ACCESS(c, command, tag, sink->index, s);
 
         pa_namereg_set_default_sink(c->protocol->core, sink);
     }
@@ -4479,6 +4534,7 @@ static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
 
         client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
         CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
+        CHECK_ACCESS(c, command, tag, idx, NULL);
 
         pa_native_connection_ref(c);
         pa_client_kill(client);
@@ -4488,6 +4544,7 @@ static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
 
         s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
         CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_ACCESS(c, command, tag, idx, NULL);
 
         pa_native_connection_ref(c);
         pa_sink_input_kill(s);
@@ -4498,6 +4555,7 @@ static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
 
         s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
         CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_ACCESS(c, command, tag, idx, NULL);
 
         pa_native_connection_ref(c);
         pa_source_output_kill(s);
@@ -4527,6 +4585,8 @@ static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag
     CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name) && !strchr(name, '/'), tag, PA_ERR_INVALID);
     CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
 
+    CHECK_ACCESS(c, command, tag, PA_INVALID_INDEX, name);
+
     if (!(m = pa_module_load(c->protocol->core, name, argument))) {
         pa_pstream_send_error(c->pstream, tag, PA_ERR_MODINITFAILED);
         return;
@@ -4555,6 +4615,8 @@ static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t t
     m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
     CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
 
+    CHECK_ACCESS(c, command, tag, idx, NULL);
+
     pa_module_unload_request(m, false);
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
@@ -4593,6 +4655,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
             sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK);
 
         CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
+        CHECK_ACCESS(c, command, tag, idx, sink->name);
 
         if (pa_sink_input_move_to(si, sink, true) < 0) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
@@ -4612,6 +4675,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
             source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE);
 
         CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
+        CHECK_ACCESS(c, command, tag, idx, source->name);
 
         if (pa_source_output_move_to(so, source, true) < 0) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
@@ -4647,6 +4711,8 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
         if (idx == PA_INVALID_INDEX && name && !*name) {
 
+            CHECK_ACCESS(c, command, tag, idx, name);
+
             pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
 
             if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
@@ -4663,6 +4729,8 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
+            CHECK_ACCESS(c, command, tag, sink->index, name);
+
             pa_log_debug("%s of sink %s requested by client %" PRIu32 ".",
                          b ? "Suspending" : "Resuming", sink->name, c->client->index);
 
@@ -4677,6 +4745,8 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
         if (idx == PA_INVALID_INDEX && name && !*name) {
 
+            CHECK_ACCESS(c, command, tag, idx, name);
+
             pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
 
             if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
@@ -4694,6 +4764,8 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
 
+            CHECK_ACCESS(c, command, tag, source->index, name);
+
             pa_log_debug("%s of source %s requested by client %" PRIu32 ".",
                          b ? "Suspending" : "Resuming", source->name, c->client->index);
 
@@ -4737,6 +4809,8 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
     CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
     CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
 
+    CHECK_ACCESS(c, command, tag, m->index, name);
+
     cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
     CHECK_VALIDITY(c->pstream, cb, tag, PA_ERR_NOEXTENSION);
 
@@ -4779,6 +4853,8 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
 
     CHECK_VALIDITY(c->pstream, profile, tag, PA_ERR_NOENTITY);
 
+    CHECK_ACCESS(c, command, tag, card->index, profile_name);
+
     if ((ret = pa_card_set_profile(card, profile, true)) < 0) {
         pa_pstream_send_error(c->pstream, tag, -ret);
         return;
@@ -4819,6 +4895,8 @@ static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command,
 
         CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
+        CHECK_ACCESS(c, command, tag, sink->index, name);
+
         if ((ret = pa_sink_set_port(sink, port, true)) < 0) {
             pa_pstream_send_error(c->pstream, tag, -ret);
             return;
@@ -4835,6 +4913,8 @@ static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command,
 
         CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
 
+        CHECK_ACCESS(c, command, tag, source->index, name);
+
         if ((ret = pa_source_set_port(source, port, true)) < 0) {
             pa_pstream_send_error(c->pstream, tag, -ret);
             return;
@@ -4879,6 +4959,8 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command,
     port = pa_hashmap_get(card->ports, port_name);
     CHECK_VALIDITY(c->pstream, port, tag, PA_ERR_NOENTITY);
 
+    CHECK_ACCESS(c, command, tag, card->index, port_name);
+
     pa_device_port_set_latency_offset(port, offset);
 
     pa_pstream_send_simple_ack(c->pstream, tag);
@@ -5419,3 +5501,136 @@ pa_client* pa_native_connection_get_client(pa_native_connection *c) {
 
     return c->client;
 }
+
+static const pa_access_hook_t map_table[PA_COMMAND_MAX] = {
+    /* CLIENT -> SERVER */
+    [PA_COMMAND_EXIT] = PA_ACCESS_HOOK_EXIT_DAEMON,
+    [PA_COMMAND_SET_DEFAULT_SINK] = PA_ACCESS_HOOK_SET_DEFAULT_SINK,
+    [PA_COMMAND_SET_DEFAULT_SOURCE] = PA_ACCESS_HOOK_SET_DEFAULT_SOURCE,
+
+    [PA_COMMAND_GET_SINK_INFO] = PA_ACCESS_HOOK_GET_SINK_INFO,
+    [PA_COMMAND_GET_SINK_INFO_LIST] = PA_ACCESS_HOOK_GET_SINK_INFO,
+    [PA_COMMAND_SET_SINK_VOLUME] = PA_ACCESS_HOOK_SET_SINK_VOLUME,
+    [PA_COMMAND_SET_SINK_MUTE] = PA_ACCESS_HOOK_SET_SINK_MUTE,
+    [PA_COMMAND_SUSPEND_SINK] = PA_ACCESS_HOOK_SUSPEND_SINK,
+    [PA_COMMAND_SET_SINK_PORT] = PA_ACCESS_HOOK_SET_SINK_PORT,
+
+    [PA_COMMAND_GET_SOURCE_INFO] = PA_ACCESS_HOOK_GET_SOURCE_INFO,
+    [PA_COMMAND_GET_SOURCE_INFO_LIST] = PA_ACCESS_HOOK_GET_SOURCE_INFO,
+    [PA_COMMAND_SET_SOURCE_VOLUME] = PA_ACCESS_HOOK_SET_SOURCE_VOLUME,
+    [PA_COMMAND_SET_SOURCE_MUTE] = PA_ACCESS_HOOK_SET_SOURCE_MUTE,
+    [PA_COMMAND_SUSPEND_SOURCE] = PA_ACCESS_HOOK_SUSPEND_SOURCE,
+    [PA_COMMAND_SET_SOURCE_PORT] = PA_ACCESS_HOOK_SET_SOURCE_PORT,
+
+    [PA_COMMAND_GET_SERVER_INFO] = PA_ACCESS_HOOK_GET_SERVER_INFO,
+
+    [PA_COMMAND_GET_MODULE_INFO] = PA_ACCESS_HOOK_GET_MODULE_INFO,
+    [PA_COMMAND_GET_MODULE_INFO_LIST] = PA_ACCESS_HOOK_GET_MODULE_INFO,
+    [PA_COMMAND_LOAD_MODULE] = PA_ACCESS_HOOK_LOAD_MODULE,
+    [PA_COMMAND_UNLOAD_MODULE] = PA_ACCESS_HOOK_UNLOAD_MODULE,
+
+    [PA_COMMAND_GET_CLIENT_INFO] = PA_ACCESS_HOOK_GET_CLIENT_INFO,
+    [PA_COMMAND_GET_CLIENT_INFO_LIST] = PA_ACCESS_HOOK_GET_CLIENT_INFO,
+    [PA_COMMAND_KILL_CLIENT] = PA_ACCESS_HOOK_KILL_CLIENT,
+
+    [PA_COMMAND_GET_CARD_INFO] = PA_ACCESS_HOOK_GET_CARD_INFO,
+    [PA_COMMAND_GET_CARD_INFO_LIST] = PA_ACCESS_HOOK_GET_CARD_INFO,
+    [PA_COMMAND_SET_CARD_PROFILE] = PA_ACCESS_HOOK_SET_CARD_PROFILE,
+    [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = PA_ACCESS_HOOK_SET_PORT_LATENCY_OFFSET,
+
+    [PA_COMMAND_GET_SINK_INPUT_INFO] = PA_ACCESS_HOOK_GET_SINK_INPUT_INFO,
+    [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = PA_ACCESS_HOOK_GET_SINK_INPUT_INFO,
+    [PA_COMMAND_MOVE_SINK_INPUT] = PA_ACCESS_HOOK_MOVE_SINK_INPUT,
+    [PA_COMMAND_SET_SINK_INPUT_VOLUME] = PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME,
+    [PA_COMMAND_SET_SINK_INPUT_MUTE] = PA_ACCESS_HOOK_SET_SINK_INPUT_MUTE,
+    [PA_COMMAND_KILL_SINK_INPUT] = PA_ACCESS_HOOK_KILL_SINK_INPUT,
+
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = PA_ACCESS_HOOK_GET_SOURCE_OUTPUT_INFO,
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = PA_ACCESS_HOOK_GET_SOURCE_OUTPUT_INFO,
+    [PA_COMMAND_MOVE_SOURCE_OUTPUT] = PA_ACCESS_HOOK_MOVE_SOURCE_OUTPUT,
+    [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME,
+    [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_MUTE,
+    [PA_COMMAND_KILL_SOURCE_OUTPUT] = PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT,
+
+    [PA_COMMAND_STAT] = PA_ACCESS_HOOK_STAT,
+
+    [PA_COMMAND_GET_SAMPLE_INFO] = PA_ACCESS_HOOK_GET_SAMPLE_INFO,
+    [PA_COMMAND_GET_SAMPLE_INFO_LIST] = PA_ACCESS_HOOK_GET_SAMPLE_INFO,
+
+    [PA_COMMAND_CREATE_UPLOAD_STREAM] = PA_ACCESS_HOOK_CONNECT_UPLOAD,
+    [PA_COMMAND_REMOVE_SAMPLE] = PA_ACCESS_HOOK_REMOVE_SAMPLE,
+    [PA_COMMAND_PLAY_SAMPLE] = PA_ACCESS_HOOK_PLAY_SAMPLE,
+
+    [PA_COMMAND_CREATE_PLAYBACK_STREAM] = PA_ACCESS_HOOK_CONNECT_PLAYBACK,
+    [PA_COMMAND_CREATE_RECORD_STREAM] = PA_ACCESS_HOOK_CONNECT_RECORD,
+
+    [PA_COMMAND_EXTENSION] = PA_ACCESS_HOOK_EXTENSION,
+
+    /* SERVER -> CLIENT */
+    [PA_COMMAND_SUBSCRIBE_EVENT] = PA_ACCESS_HOOK_FILTER_SUBSCRIBE_EVENT
+};
+
+typedef struct pa_protocol_native_access_data {
+    pa_access_data d;
+
+    pa_pdispatch *pd;
+    uint32_t command;
+    uint32_t tag;
+    pa_tagstruct *tc;
+    void *userdata;
+} pa_protocol_native_access_data;
+
+static void check_access_finish_cb(pa_access_data *data, bool res) {
+    pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data;
+    pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata);
+
+    if (!res) {
+        pa_pstream_send_error(c->pstream, d->tag, PA_ERR_ACCESS); \
+        goto finish;
+    }
+
+    /* call the dispatcher again, hopefully this time, the access check will
+     * fail or succeed immediately */
+    command_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata);
+
+finish:
+    pa_xfree((char *)d->d.name);
+    if (d->pd)
+        pa_pdispatch_unref(d->pd);
+    if (d->tc)
+        pa_tagstruct_free(d->tc);
+    pa_xfree(d);
+}
+
+static pa_hook_result_t check_access(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata, uint32_t idx, pa_subscription_event_type_t event, const char *name) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_hook_result_t res;
+
+    if (map_table[command]) {
+        pa_protocol_native_access_data *data;
+
+        data = pa_xnew0 (pa_protocol_native_access_data, 1);
+        data->d.client_index = c->client->index;
+        data->d.object_index = idx;
+        data->d.event = event;
+        data->d.name = name;
+        data->d.hook = map_table[command];
+
+        res = pa_hook_fire(&c->protocol->core->access[data->d.hook], data);
+        if (res == PA_HOOK_CANCEL) {
+            /* async */
+            data->d.name = pa_xstrdup(name);
+            data->d.async_finish_cb = check_access_finish_cb;
+            data->pd = pd ? pa_pdispatch_ref (pd) : NULL;
+            data->command = command;
+            data->tag = tag;
+            data->tc = t ? pa_tagstruct_copy (t) : NULL;
+            data->userdata = userdata;
+        } else {
+            pa_xfree(data);
+        }
+    } else
+        res = PA_HOOK_OK;
+
+    return res;
+}
-- 
2.1.0



More information about the pulseaudio-discuss mailing list