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