From 3f264b2c4acfaaf8dd9c6b05526708a1d7648db0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Feb 2006 15:12:42 +0000 Subject: add support for authentication using SCM_CREDENTIALS git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@596 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-tunnel.c | 4 +- src/polyp/context.c | 6 +- src/polyp/subscribe.c | 6 +- src/polypcore/iochannel.c | 130 ++++++++++++++++++++++++++++++++++++++ src/polypcore/iochannel.h | 11 ++++ src/polypcore/pdispatch.c | 21 +++++-- src/polypcore/pdispatch.h | 6 +- src/polypcore/protocol-native.c | 93 ++++++++++++++++++---------- src/polypcore/pstream-util.c | 4 +- src/polypcore/pstream-util.h | 4 +- src/polypcore/pstream.c | 134 +++++++++++++++++++++++++++++----------- src/polypcore/pstream.h | 14 +++-- 12 files changed, 342 insertions(+), 91 deletions(-) (limited to 'src') diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index df9c51fb..70bded6c 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -431,11 +431,11 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) { } -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void*creds, void *userdata) { struct userdata *u = userdata; assert(p && packet && u); - if (pa_pdispatch_run(u->pdispatch, packet, u) < 0) { + if (pa_pdispatch_run(u->pdispatch, packet, creds, u) < 0) { pa_log(__FILE__": invalid packet"); die(u); } diff --git a/src/polyp/context.c b/src/polyp/context.c index 8e999225..59079cb0 100644 --- a/src/polyp/context.c +++ b/src/polyp/context.c @@ -267,7 +267,7 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) { pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED); } -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata) { pa_context *c = userdata; assert(p); @@ -276,7 +276,7 @@ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *user pa_context_ref(c); - if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) + if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) pa_context_fail(c, PA_ERR_PROTOCOL); pa_context_unref(c); @@ -401,7 +401,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) { t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag); pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); - pa_pstream_send_tagstruct(c->pstream, t); + pa_pstream_send_tagstruct_with_creds(c->pstream, t, 1); pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); diff --git a/src/polyp/subscribe.c b/src/polyp/subscribe.c index a4eadbc6..110d4e52 100644 --- a/src/polyp/subscribe.c +++ b/src/polyp/subscribe.c @@ -36,7 +36,7 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_context *c = userdata; pa_subscription_event_type_t e; - uint32_t index; + uint32_t idx; assert(pd); assert(command == PA_COMMAND_SUBSCRIBE_EVENT); @@ -46,14 +46,14 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSE pa_context_ref(c); if (pa_tagstruct_getu32(t, &e) < 0 || - pa_tagstruct_getu32(t, &index) < 0 || + pa_tagstruct_getu32(t, &idx) < 0 || !pa_tagstruct_eof(t)) { pa_context_fail(c, PA_ERR_PROTOCOL); goto finish; } if (c->subscribe_callback) - c->subscribe_callback(c, e, index, c->subscribe_userdata); + c->subscribe_callback(c, e, idx, c->subscribe_userdata); finish: pa_context_unref(c); diff --git a/src/polypcore/iochannel.c b/src/polypcore/iochannel.c index 89b061c2..ea0ac988 100644 --- a/src/polypcore/iochannel.c +++ b/src/polypcore/iochannel.c @@ -27,12 +27,14 @@ #include #include #include +#include #include "winsock.h" #include #include #include +#include #include "iochannel.h" @@ -242,6 +244,134 @@ ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) { return r; } +#ifdef SCM_CREDENTIALS + +int pa_iochannel_creds_supported(pa_iochannel *io) { + struct sockaddr_un sa; + socklen_t l; + + assert(io); + assert(io->ifd >= 0); + assert(io->ofd == io->ifd); + + l = sizeof(sa); + + if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0) + return 0; + + return sa.sun_family == AF_UNIX; +} + +int pa_iochannel_creds_enable(pa_iochannel *io) { + int t = 1; + + assert(io); + assert(io->ifd >= 0); + + if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) { + pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", strerror(errno)); + return -1; + } + + return 0; +} + +ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l) { + ssize_t r; + struct msghdr mh; + struct iovec iov; + uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))]; + struct ucred *ucred; + struct cmsghdr *cmsg; + + assert(io); + assert(data); + assert(l); + assert(io->ofd >= 0); + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = (void*) data; + iov.iov_len = l; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + cmsg = (struct cmsghdr*) cmsg_data; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + + ucred = (struct ucred*) CMSG_DATA(cmsg); + ucred->pid = getpid(); + ucred->uid = getuid(); + ucred->gid = getgid(); + + memset(&mh, 0, sizeof(mh)); + mh.msg_name = NULL; + mh.msg_namelen = 0; + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = cmsg_data; + mh.msg_controllen = sizeof(cmsg_data); + mh.msg_flags = 0; + + if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) { + io->writable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid) { + ssize_t r; + struct msghdr mh; + struct iovec iov; + uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))]; + + assert(io); + assert(data); + assert(l); + assert(io->ifd >= 0); + assert(ucred); + assert(creds_valid); + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = data; + iov.iov_len = l; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + + memset(&mh, 0, sizeof(mh)); + mh.msg_name = NULL; + mh.msg_namelen = 0; + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = cmsg_data; + mh.msg_controllen = sizeof(cmsg_data); + mh.msg_flags = 0; + + if ((r = recvmsg(io->ifd, &mh, MSG_NOSIGNAL)) >= 0) { + struct cmsghdr *cmsg; + + *creds_valid = 0; + + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) { + + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { + assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))); + memcpy(ucred, CMSG_DATA(cmsg), sizeof(struct ucred)); + *creds_valid = 1; + break; + } + } + + io->readable = 0; + enable_mainloop_sources(io); + } + + return r; +} +#endif + void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) { assert(io); diff --git a/src/polypcore/iochannel.h b/src/polypcore/iochannel.h index 977fe2c3..617ce086 100644 --- a/src/polypcore/iochannel.h +++ b/src/polypcore/iochannel.h @@ -23,6 +23,9 @@ ***/ #include +#include +#include + #include /* A wrapper around UNIX file descriptors for attaching them to the a @@ -48,6 +51,14 @@ void pa_iochannel_free(pa_iochannel*io); ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l); ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l); +#ifdef SCM_CREDENTIALS +int pa_iochannel_creds_supported(pa_iochannel *io); +int pa_iochannel_creds_enable(pa_iochannel *io); + +ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l); +ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid); +#endif + int pa_iochannel_is_readable(pa_iochannel*io); int pa_iochannel_is_writable(pa_iochannel*io); int pa_iochannel_is_hungup(pa_iochannel*io); diff --git a/src/polypcore/pdispatch.c b/src/polypcore/pdispatch.c index 56a21bd6..a4e58f8c 100644 --- a/src/polypcore/pdispatch.c +++ b/src/polypcore/pdispatch.c @@ -109,6 +109,7 @@ struct pa_pdispatch { PA_LLIST_HEAD(struct reply_info, replies); pa_pdispatch_drain_callback drain_callback; void *drain_userdata; + const void *creds; }; static void reply_info_free(struct reply_info *r) { @@ -136,7 +137,8 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_ PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies); pd->drain_callback = NULL; pd->drain_userdata = NULL; - + pd->creds = NULL; + return pd; } @@ -171,7 +173,7 @@ static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_pdispatch_unref(pd); } -int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) { +int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const void *creds, void *userdata) { uint32_t tag, command; pa_tagstruct *ts = NULL; int ret = -1; @@ -188,18 +190,20 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) { if (pa_tagstruct_getu32(ts, &command) < 0 || pa_tagstruct_getu32(ts, &tag) < 0) goto finish; - + #ifdef DEBUG_OPCODES { char t[256]; char const *p; if (!(p = command_names[command])) snprintf((char*) (p = t), sizeof(t), "%u", command); - + pa_log(__FILE__": Recieved opcode <%s>", p); } #endif + pd->creds = creds; + if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) { struct reply_info *r; @@ -222,6 +226,8 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) { ret = 0; finish: + pd->creds = NULL; + if (ts) pa_tagstruct_free(ts); @@ -295,3 +301,10 @@ pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) { pd->ref++; return pd; } + +const void * pa_pdispatch_creds(pa_pdispatch *pd) { + assert(pd); + assert(pd->ref >= 1); + + return pd->creds; +} diff --git a/src/polypcore/pdispatch.h b/src/polypcore/pdispatch.h index 31533d57..aa898abf 100644 --- a/src/polypcore/pdispatch.h +++ b/src/polypcore/pdispatch.h @@ -30,21 +30,23 @@ typedef struct pa_pdispatch pa_pdispatch; typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata); pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_cb_t*table, unsigned entries); void pa_pdispatch_unref(pa_pdispatch *pd); pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd); -int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, void *userdata); +int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, const void*creds, void *userdata); void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t callback, void *userdata); int pa_pdispatch_is_pending(pa_pdispatch *pd); -typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata); void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata); /* Remove all reply slots with the give userdata parameter */ void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata); +const void * pa_pdispatch_creds(pa_pdispatch *pd); + #endif diff --git a/src/polypcore/protocol-native.c b/src/polypcore/protocol-native.c index 52eaed4f..0fb9339b 100644 --- a/src/polypcore/protocol-native.c +++ b/src/polypcore/protocol-native.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -357,7 +358,7 @@ static struct playback_stream* playback_stream_new( pa_cvolume *volume, uint32_t syncid) { - struct playback_stream *s, *sync; + struct playback_stream *s, *ssync; pa_sink_input *sink_input; pa_memblock *silence; uint32_t idx; @@ -366,17 +367,17 @@ static struct playback_stream* playback_stream_new( assert(c && sink && ss && name && maxlength); /* Find syncid group */ - for (sync = pa_idxset_first(c->output_streams, &idx); sync; sync = pa_idxset_next(c->output_streams, &idx)) { + for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) { - if (sync->type != PLAYBACK_STREAM) + if (ssync->type != PLAYBACK_STREAM) continue; - if (sync->syncid == syncid) + if (ssync->syncid == syncid) break; } /* Synced streams must connect to the same sink */ - if (sync && sync->sink_input->sink != sink) + if (ssync && ssync->sink_input->sink != sink) return NULL; if (!(sink_input = pa_sink_input_new(sink, __FILE__, name, ss, map, 0, -1))) @@ -397,16 +398,16 @@ static struct playback_stream* playback_stream_new( s->sink_input->owner = c->protocol->module; s->sink_input->client = c->client; - if (sync) { + if (ssync) { /* Sync id found, now find head of list */ - PA_LLIST_FIND_HEAD(struct playback_stream, sync, &sync); + PA_LLIST_FIND_HEAD(struct playback_stream, ssync, &ssync); /* Prepend ourselves */ - PA_LLIST_PREPEND(struct playback_stream, sync, s); + PA_LLIST_PREPEND(struct playback_stream, ssync, s); /* Set our start index to the current read index of the other grozp member(s) */ - assert(sync->next); - start_index = pa_memblockq_get_read_index(sync->next->memblockq); + assert(ssync->next); + start_index = pa_memblockq_get_read_index(ssync->next->memblockq); } else { /* This ia a new sync group */ PA_LLIST_INIT(struct playback_stream, s); @@ -871,8 +872,29 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t } if (!c->authorized) { - if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) { - pa_log(__FILE__": Denied access to client with invalid authorization key."); + int success = 0; + +#ifdef SCM_CREDENTIALS + const struct ucred *ucred = pa_pdispatch_creds(pd); + + if (ucred) { + if (ucred->uid == getuid()) + success = 1; + + pa_log_info(__FILE__": Got credentials: pid=%lu uid=%lu gid=%lu auth=%i", + (unsigned long) ucred->pid, + (unsigned long) ucred->uid, + (unsigned long) ucred->gid, + success); + + } +#endif + + if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0) + success = 1; + + if (!success) { + pa_log_warn(__FILE__": Denied access to client with invalid authorization data."); pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS); return; } @@ -1589,7 +1611,7 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ struct connection *c = userdata; uint32_t idx; int b; - struct playback_stream *s, *sync; + struct playback_stream *s, *ssync; assert(c && t); if (pa_tagstruct_getu32(t, &idx) < 0 || @@ -1609,14 +1631,14 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ pa_memblockq_prebuf_force(s->memblockq); /* Do the same for all other members in the sync group */ - for (sync = s->prev; sync; sync = sync->prev) { - pa_sink_input_cork(sync->sink_input, b); - pa_memblockq_prebuf_force(sync->memblockq); + for (ssync = s->prev; ssync; ssync = ssync->prev) { + pa_sink_input_cork(ssync->sink_input, b); + pa_memblockq_prebuf_force(ssync->memblockq); } - for (sync = s->next; sync; sync = sync->next) { - pa_sink_input_cork(sync->sink_input, b); - pa_memblockq_prebuf_force(sync->memblockq); + for (ssync = s->next; ssync; ssync = ssync->next) { + pa_sink_input_cork(ssync->sink_input, b); + pa_memblockq_prebuf_force(ssync->memblockq); } pa_pstream_send_simple_ack(c->pstream, tag); @@ -1625,7 +1647,7 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ static void command_flush_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct connection *c = userdata; uint32_t idx; - struct playback_stream *s, *sync; + struct playback_stream *s, *ssync; assert(c && t); if (pa_tagstruct_getu32(t, &idx) < 0 || @@ -1644,25 +1666,25 @@ static void command_flush_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC s->underrun = 0; /* Do the same for all other members in the sync group */ - for (sync = s->prev; sync; sync = sync->prev) { - pa_memblockq_flush(sync->memblockq); - sync->underrun = 0; + for (ssync = s->prev; ssync; ssync = ssync->prev) { + pa_memblockq_flush(ssync->memblockq); + ssync->underrun = 0; } - for (sync = s->next; sync; sync = sync->next) { - pa_memblockq_flush(sync->memblockq); - sync->underrun = 0; + for (ssync = s->next; ssync; ssync = ssync->next) { + pa_memblockq_flush(ssync->memblockq); + ssync->underrun = 0; } pa_pstream_send_simple_ack(c->pstream, tag); pa_sink_notify(s->sink_input->sink); request_bytes(s); - for (sync = s->prev; sync; sync = sync->prev) - request_bytes(sync); + for (ssync = s->prev; ssync; ssync = ssync->prev) + request_bytes(ssync); - for (sync = s->next; sync; sync = sync->next) - request_bytes(sync); + for (ssync = s->next; ssync; ssync = ssync->next) + request_bytes(ssync); } static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -2017,11 +2039,11 @@ static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC /*** pstream callbacks ***/ -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata) { struct connection *c = userdata; assert(p && packet && packet->data && c); - if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { + if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) { pa_log(__FILE__": invalid packet."); connection_free(c); } @@ -2183,6 +2205,13 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo c->subscription = NULL; pa_idxset_put(p->connections, c, NULL); + + +#ifdef SCM_CREDENTIALS + if (pa_iochannel_creds_supported(io)) + pa_iochannel_creds_enable(io); + +#endif } /*** module entry points ***/ diff --git a/src/polypcore/pstream-util.c b/src/polypcore/pstream-util.c index 62986456..bd1d1a87 100644 --- a/src/polypcore/pstream-util.c +++ b/src/polypcore/pstream-util.c @@ -29,7 +29,7 @@ #include "pstream-util.h" -void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) { +void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, int creds) { size_t length; uint8_t *data; pa_packet *packet; @@ -40,7 +40,7 @@ void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) { assert(data && length); packet = pa_packet_new_dynamic(data, length); assert(packet); - pa_pstream_send_packet(p, packet); + pa_pstream_send_packet(p, packet, creds); pa_packet_unref(packet); } diff --git a/src/polypcore/pstream-util.h b/src/polypcore/pstream-util.h index c400c6d8..f2677a44 100644 --- a/src/polypcore/pstream-util.h +++ b/src/polypcore/pstream-util.h @@ -27,7 +27,9 @@ #include /* The tagstruct is freed!*/ -void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t); +void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, int creds); + +#define pa_pstream_send_tagstruct(p, t) pa_pstream_send_tagstruct_with_creds((p), (t), 0) void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error); void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag); diff --git a/src/polypcore/pstream.c b/src/polypcore/pstream.c index b1e8bd06..b93dca08 100644 --- a/src/polypcore/pstream.c +++ b/src/polypcore/pstream.c @@ -65,6 +65,9 @@ struct item_info { /* packet info */ pa_packet *packet; +#ifdef SCM_CREDENTIALS + int with_creds; +#endif }; struct pa_pstream { @@ -76,8 +79,6 @@ struct pa_pstream { pa_queue *send_queue; int dead; - void (*die_callback) (pa_pstream *p, void *userdata); - void *die_callback_userdata; struct { struct item_info* current; @@ -94,16 +95,25 @@ struct pa_pstream { size_t index; } read; - void (*recieve_packet_callback) (pa_pstream *p, pa_packet *packet, void *userdata); + pa_pstream_packet_cb_t recieve_packet_callback; void *recieve_packet_callback_userdata; - void (*recieve_memblock_callback) (pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata); + pa_pstream_memblock_cb_t recieve_memblock_callback; void *recieve_memblock_callback_userdata; - void (*drain_callback)(pa_pstream *p, void *userdata); - void *drain_userdata; + pa_pstream_notify_cb_t drain_callback; + void *drain_callback_userdata; + + pa_pstream_notify_cb_t die_callback; + void *die_callback_userdata; pa_memblock_stat *memblock_stat; + +#ifdef SCM_CREDENTIALS + int send_creds_now; + struct ucred ucred; + int creds_valid; +#endif }; static int do_write(pa_pstream *p); @@ -170,8 +180,6 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_sta pa_iochannel_set_callback(io, io_callback, p); p->dead = 0; - p->die_callback = NULL; - p->die_callback_userdata = NULL; p->mainloop = m; p->defer_event = m->defer_new(m, defer_callback, p); @@ -194,13 +202,20 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_sta p->recieve_memblock_callback_userdata = NULL; p->drain_callback = NULL; - p->drain_userdata = NULL; + p->drain_callback_userdata = NULL; + + p->die_callback = NULL; + p->die_callback_userdata = NULL; p->memblock_stat = s; pa_iochannel_socket_set_rcvbuf(io, 1024*8); - pa_iochannel_socket_set_sndbuf(io, 1024*8); + pa_iochannel_socket_set_sndbuf(io, 1024*8); +#ifdef SCM_CREDENTIALS + p->send_creds_now = 0; + p->creds_valid = 0; +#endif return p; } @@ -239,7 +254,7 @@ static void pstream_free(pa_pstream *p) { pa_xfree(p); } -void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) { +void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, int with_creds) { struct item_info *i; assert(p && packet && p->ref >= 1); @@ -251,6 +266,9 @@ void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) { i = pa_xnew(struct item_info, 1); i->type = PA_PSTREAM_ITEM_PACKET; i->packet = pa_packet_ref(packet); +#ifdef SCM_CREDENTIALS + i->with_creds = with_creds; +#endif pa_queue_push(p->send_queue, i); p->mainloop->defer_enable(p->defer_event, 1); @@ -278,20 +296,6 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa p->mainloop->defer_enable(p->defer_event, 1); } -void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata) { - assert(p && callback); - - p->recieve_packet_callback = callback; - p->recieve_packet_callback_userdata = userdata; -} - -void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, int64_t delta, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata), void *userdata) { - assert(p && callback); - - p->recieve_memblock_callback = callback; - p->recieve_memblock_callback_userdata = userdata; -} - static void prepare_next_write_item(pa_pstream *p) { assert(p); @@ -310,6 +314,11 @@ static void prepare_next_write_item(pa_pstream *p) { p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = 0; p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0; p->write.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK] = 0; + +#ifdef SCM_CREDENTIALS + p->send_creds_now = 1; +#endif + } else { assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock); p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index; @@ -318,6 +327,10 @@ static void prepare_next_write_item(pa_pstream *p) { p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32)); p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset)); p->write.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK] = htonl(p->write.current->seek_mode); + +#ifdef SCM_CREDENTIALS + p->send_creds_now = 1; +#endif } } @@ -343,6 +356,16 @@ static int do_write(pa_pstream *p) { l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE); } +#ifdef SCM_CREDENTIALS + if (p->send_creds_now) { + + if ((r = pa_iochannel_write_with_creds(p->io, d, l)) < 0) + return -1; + + p->send_creds_now = 0; + } else +#endif + if ((r = pa_iochannel_write(p->io, d, l)) < 0) return -1; @@ -354,7 +377,7 @@ static int do_write(pa_pstream *p) { p->write.current = NULL; if (p->drain_callback && !pa_pstream_is_pending(p)) - p->drain_callback(p, p->drain_userdata); + p->drain_callback(p, p->drain_callback_userdata); } return 0; @@ -375,8 +398,19 @@ static int do_read(pa_pstream *p) { l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE); } +#ifdef SCM_CREDENTIALS + { + int b; + + if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->ucred, &b)) <= 0) + return -1; + + p->creds_valid = p->creds_valid || b; + } +#else if ((r = pa_iochannel_read(p->io, d, l)) <= 0) return -1; +#endif p->read.index += r; @@ -453,40 +487,66 @@ static int do_read(pa_pstream *p) { assert(p->read.packet); if (p->recieve_packet_callback) - p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata); +#ifdef SCM_CREDENTIALS + p->recieve_packet_callback(p, p->read.packet, p->creds_valid ? &p->ucred : NULL, p->recieve_packet_callback_userdata); +#else + p->recieve_packet_callback(p, p->read.packet, NULL, p->recieve_packet_callback_userdata); +#endif pa_packet_unref(p->read.packet); p->read.packet = NULL; } p->read.index = 0; +#ifdef SCM_CREDENTIALS + p->creds_valid = 0; +#endif } } return 0; } -void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata) { - assert(p && callback); - p->die_callback = callback; +void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) { + assert(p); + assert(p->ref >= 1); + + p->die_callback = cb; p->die_callback_userdata = userdata; } -int pa_pstream_is_pending(pa_pstream *p) { + +void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) { assert(p); + assert(p->ref >= 1); - if (p->dead) - return 0; + p->drain_callback = cb; + p->drain_callback_userdata = userdata; +} - return p->write.current || !pa_queue_is_empty(p->send_queue); +void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) { + assert(p); + assert(p->ref >= 1); + + p->recieve_packet_callback = cb; + p->recieve_packet_callback_userdata = userdata; } -void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata) { +void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) { assert(p); assert(p->ref >= 1); - p->drain_callback = cb; - p->drain_userdata = userdata; + p->recieve_memblock_callback = cb; + p->recieve_memblock_callback_userdata = userdata; +} + +int pa_pstream_is_pending(pa_pstream *p) { + assert(p); + + if (p->dead) + return 0; + + return p->write.current || !pa_queue_is_empty(p->send_queue); } void pa_pstream_unref(pa_pstream*p) { diff --git a/src/polypcore/pstream.h b/src/polypcore/pstream.h index 741ba9b5..feb1b151 100644 --- a/src/polypcore/pstream.h +++ b/src/polypcore/pstream.h @@ -33,18 +33,22 @@ typedef struct pa_pstream pa_pstream; +typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata); +typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata); +typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata); + pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s); void pa_pstream_unref(pa_pstream*p); pa_pstream* pa_pstream_ref(pa_pstream*p); -void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet); +void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, int with_creds); void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk); -void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata); -void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata), void *userdata); -void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata); +void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata); +void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata); +void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata); -void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata); +void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata); int pa_pstream_is_pending(pa_pstream *p); -- cgit