>From 95c96939b6c4b5ad19105978c2c8725dde348795 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 29 Apr 2014 23:32:11 +0200 Subject: [PATCH 5/9] SBUS: Retrieve caller UID for all messages on the system bus Before dispatching any message arriving on the system bus, we run an async request to pair the sender identifier with a UID. This is an asynchronous version of dbus_connection_get_unix_user(). FIXME: * need to support multiple concurrent requests for the same caller and not create a separate request * need to flush the cache on NameOwnerChanged * Should we carry on when we can't retrieve the ID for one reason or another and let the callee kick out the request? * !! Unit test --- src/responder/ifp/ifpsrv.c | 2 +- src/sbus/sssd_dbus.h | 5 +- src/sbus/sssd_dbus_connection.c | 76 +++++++++++++++- src/sbus/sssd_dbus_private.h | 11 +++ src/sbus/sssd_dbus_request.c | 189 ++++++++++++++++++++++++++++++++++++++++ src/util/util_errors.c | 2 + src/util/util_errors.h | 2 + 7 files changed, 281 insertions(+), 6 deletions(-) diff --git a/src/responder/ifp/ifpsrv.c b/src/responder/ifp/ifpsrv.c index e76f3bdb0afb997ee2bbc0122199e127da34de84..2f98cf838c6bfe66d0ef15d62e2c7516c88dab90 100644 --- a/src/responder/ifp/ifpsrv.c +++ b/src/responder/ifp/ifpsrv.c @@ -150,7 +150,7 @@ sysbus_init(TALLOC_CTX *mem_ctx, /* Integrate with tevent loop */ ret = sbus_init_connection(system_bus, ev, conn, - SBUS_CONN_TYPE_SHARED, + SBUS_CONN_TYPE_SYSBUS, &system_bus->conn); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h index 1604f6e631b258e89fa8b0d359df4d13b7c4a0d8..6f1670cfe1b3f8863bf603e9be0387674812287e 100644 --- a/src/sbus/sssd_dbus.h +++ b/src/sbus/sssd_dbus.h @@ -27,6 +27,7 @@ struct sbus_interface; struct sbus_request; #include +#include typedef int (*sbus_msg_handler_fn)(struct sbus_request *dbus_req, void *instance_data); @@ -49,7 +50,8 @@ typedef int (*sbus_server_conn_init_fn)(struct sbus_connection *, void *); enum { SBUS_CONN_TYPE_PRIVATE = 1, - SBUS_CONN_TYPE_SHARED + SBUS_CONN_TYPE_SHARED, + SBUS_CONN_TYPE_SYSBUS }; enum { @@ -185,6 +187,7 @@ void sbus_conn_send_reply(struct sbus_connection *conn, * or sbus_request_fail() functions. */ struct sbus_request { + uid_t client; struct sbus_connection *conn; DBusMessage *message; struct sbus_interface *intf; diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c index 7ea70efa55a4898c64360d8c3e9c308d026a61fe..61883c625ea92a4cf8206b04e83af61e15762aee 100644 --- a/src/sbus/sssd_dbus_connection.c +++ b/src/sbus/sssd_dbus_connection.c @@ -174,6 +174,13 @@ int sbus_init_connection(TALLOC_CTX *ctx, conn->dbus.conn = dbus_conn; conn->connection_type = connection_type; + ret = sss_hash_create(conn, 32, &conn->clients); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create clients hash table\n"); + talloc_free(conn); + return EIO; + } + ret = sbus_conn_set_fns(conn); if (ret != EOK) { talloc_free(conn); @@ -275,8 +282,10 @@ static int connection_destructor(void *ctx) /* Private connections must be closed explicitly */ dbus_connection_close(conn->dbus.conn); } - else if (conn->connection_type == SBUS_CONN_TYPE_SHARED) { - /* Shared connections are destroyed when their last reference is removed */ + else if (conn->connection_type == SBUS_CONN_TYPE_SHARED || + conn->connection_type == SBUS_CONN_TYPE_SYSBUS) { + /* Shared and system bus connections are destroyed when their last + reference is removed */ } else { /* Critical Error! */ @@ -350,6 +359,15 @@ void sbus_disconnect(struct sbus_connection *conn) #define VTABLE_FUNC(vtable, offset) \ (*((void **)((char *)(vtable) + (offset)))) +struct sbus_unhandled_request { + struct sbus_request *dbus_req; + const struct sbus_method_meta *method; + sbus_msg_handler_fn handler_fn; + void *handler_data; +}; + +static void sbus_handler_got_caller_id(struct tevent_req *req); + /* messsage_handler * Receive messages and process them */ @@ -361,6 +379,7 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn, const char *msg_method; const char *path; const char *msg_interface; + const char *sender; DBusMessage *reply = NULL; const struct sbus_method_meta *method; const struct sbus_interface_meta *interface; @@ -369,6 +388,8 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn, void *handler_data = NULL; /* Must be a talloc pointer! */ struct sbus_introspect_ctx *ictx = NULL; DBusHandlerResult result; + struct tevent_req *req; + struct sbus_unhandled_request *uh_req; if (!user_data) { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -379,6 +400,7 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn, DEBUG(SSSDBG_TRACE_ALL, "Received SBUS method [%s]\n", msg_method); path = dbus_message_get_path(message); msg_interface = dbus_message_get_interface(message); + sender = dbus_message_get_sender(message); if (!msg_method || !path || !msg_interface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -453,8 +475,30 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn, handler_data = intf_p->intf->instance_data; } - sbus_request_invoke_or_finish(dbus_req, handler_fn, handler_data, - method->invoker); + uh_req = talloc(dbus_req, struct sbus_unhandled_request); + if (uh_req == NULL) { + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, NULL); + sbus_conn_send_reply(intf_p->conn, reply); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + + uh_req->dbus_req = dbus_req; + uh_req->handler_fn = handler_fn; + uh_req->handler_data = handler_data; + uh_req->method = method; + + req = sbus_get_sender_id_send(dbus_req, dbus_req->conn->ev, + dbus_req->conn, sender); + if (req == NULL) { + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, NULL); + sbus_conn_send_reply(intf_p->conn, reply); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + tevent_req_set_callback(req, sbus_handler_got_caller_id, uh_req); result = DBUS_HANDLER_RESULT_HANDLED; } } @@ -462,6 +506,30 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn, return result; } +static void sbus_handler_got_caller_id(struct tevent_req *req) +{ + struct sbus_unhandled_request *uh_req = \ + tevent_req_callback_data(req, + struct sbus_unhandled_request); + errno_t ret; + DBusError *dberr; + + ret = sbus_get_sender_id_recv(req, &uh_req->dbus_req->client); + if (ret != EOK) { + dberr = sbus_error_new(uh_req->dbus_req, + DBUS_ERROR_FAILED, + "Failed to retrieve called ID: %s\n", + sss_strerror(ret)); + sbus_request_fail_and_finish(uh_req->dbus_req, dberr); + return; + } + + sbus_request_invoke_or_finish(uh_req->dbus_req, + uh_req->handler_fn, + uh_req->handler_data, + uh_req->method->invoker); +} + struct sbus_interface * sbus_new_interface(TALLOC_CTX *mem_ctx, const char *object_path, diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h index 58b385b71d5050a9afbe45232378bc62835eb72b..48fbbc4f75a5a52fe395ee8ac2d41ef355e41197 100644 --- a/src/sbus/sssd_dbus_private.h +++ b/src/sbus/sssd_dbus_private.h @@ -22,6 +22,8 @@ #ifndef _SSSD_DBUS_PRIVATE_H_ #define _SSSD_DBUS_PRIVATE_H_ +#include + #include "sssd_dbus_meta.h" union dbus_conn_pointer { @@ -60,6 +62,7 @@ struct sbus_connection { char *symlink; sbus_server_conn_init_fn srv_init_fn; void *srv_init_data; + hash_table_t *clients; /* watches list */ struct sbus_watch_ctx *watch_list; @@ -126,4 +129,12 @@ int sss_dbus_conn_send(DBusConnection *dbus_conn, void *pvt, DBusPendingCall **pending); + +/* =Retrieve-conn-credentials=============================================== */ +struct tevent_req *sbus_get_sender_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_connection *conn, + const char *sender); +int sbus_get_sender_id_recv(struct tevent_req *req, uid_t *_uid); + #endif /* _SSSD_DBUS_PRIVATE_H_ */ diff --git a/src/sbus/sssd_dbus_request.c b/src/sbus/sssd_dbus_request.c index 0021ce0e9e1e102b99c11fea3fc67669c3dac88e..b49ca117f6a989b9cad7ad2c617702024727b3d9 100644 --- a/src/sbus/sssd_dbus_request.c +++ b/src/sbus/sssd_dbus_request.c @@ -276,3 +276,192 @@ sbus_request_parse_or_finish(struct sbus_request *request, return ret; } + +struct sbus_get_sender_id_state { + struct sbus_connection *conn; + DBusConnection *sysbus_conn; + const char *sender; + uid_t uid; +}; + +static void sbus_get_sender_id_done(DBusPendingCall *pending, void *ptr); + +struct tevent_req *sbus_get_sender_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_connection *conn, + const char *sender) +{ + struct tevent_req *req; + struct sbus_get_sender_id_state *state; + DBusError dbus_error; + DBusMessage *msg = NULL; + dbus_bool_t dbret; + errno_t ret; + hash_key_t key; + hash_value_t value; + + req = tevent_req_create(mem_ctx, &state, struct sbus_get_sender_id_state); + if (req == NULL) { + return NULL; + } + state->sender = sender; + state->conn = conn; + + if (conn->connection_type != SBUS_CONN_TYPE_SYSBUS) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Not a sysbus message, quit\n"); + ret = EOK; + goto immediate; + } + + if (sender == NULL) { + ret = ERR_SBUS_NO_SENDER; + goto immediate; + } + + key.type = HASH_KEY_STRING; + key.str = discard_const(sender); + ret = hash_lookup(conn->clients, &key, &value); + if (ret == HASH_SUCCESS) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "%s already present in the clients table\n", sender); + ret = EOK; + goto immediate; + } else if (ret != HASH_ERROR_KEY_NOT_FOUND) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to look up %s in the clients table\n", sender); + ret = ERR_SBUS_GET_SENDER_ERROR; + goto immediate; + } + + /* We don't know this sender yet, let's ask the system bus */ + + /* Connect to the well-known system bus */ + dbus_error_init(&dbus_error); + state->sysbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error); + if (state->sysbus_conn == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to connect to D-BUS system bus.\n"); + ret = ERR_SBUS_GET_SENDER_ERROR; + goto immediate; + } + dbus_connection_set_exit_on_disconnect(state->sysbus_conn, FALSE); + + msg = dbus_message_new_method_call("org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "GetConnectionUnixUser"); + if (msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); + ret = ENOMEM; + goto immediate; + } + + dbret = dbus_message_append_args(msg, + DBUS_TYPE_STRING, &sender, + DBUS_TYPE_INVALID); + if (!dbret) { + goto immediate; + } + + ret = sss_dbus_conn_send(state->sysbus_conn, msg, 3000, + sbus_get_sender_id_done, + req, NULL); + dbus_message_unref(msg); + msg = NULL; + if (ret != EOK) { + goto immediate; + } + + return req; + +immediate: + if (ret == EOK) { + tevent_req_done(req); + } else { + if (msg != NULL) { + dbus_message_unref(msg); + } + if (state->sysbus_conn != NULL) { + dbus_connection_unref(state->sysbus_conn); + } + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + return req; +} + +static void sbus_get_sender_id_done(DBusPendingCall *pending, void *ptr) +{ + struct tevent_req *req; + struct sbus_get_sender_id_state *state; + DBusMessage *reply; + DBusError dbus_error; + hash_key_t key; + hash_value_t value; + dbus_bool_t dbret; + int ret; + + dbus_error_init(&dbus_error); + + req = talloc_get_type(ptr, struct tevent_req); + state = tevent_req_data(req, struct sbus_get_sender_id_state); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Severe error. A reply callback was called but no reply " + "was received and no timeout occurred\n"); + + /* FIXME: Destroy this connection ? */ + ret = EIO; + goto done; + } + + dbret = dbus_message_get_args(reply, + &dbus_error, + DBUS_TYPE_UINT32, &state->uid, + DBUS_TYPE_INVALID); + if (!dbret) { + ret = EIO; + goto done; + } + + key.type = HASH_KEY_STRING; + key.str = discard_const(state->sender); + value.type = HASH_VALUE_UINT; + value.ul = state->uid; + ret = hash_enter(state->conn->clients, &key, &value); + if (ret != HASH_SUCCESS) { + ret = EIO; + goto done; + } + + ret = EOK; +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); + dbus_connection_unref(state->sysbus_conn); + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } +} + +int sbus_get_sender_id_recv(struct tevent_req *req, uid_t *_uid) +{ + struct sbus_get_sender_id_state *state = \ + tevent_req_data(req, struct sbus_get_sender_id_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_uid) { + *_uid = state->uid; + } + + return EOK; +} diff --git a/src/util/util_errors.c b/src/util/util_errors.c index d27d20b0a511cfd9090707c62064c7d11ebc68cc..90faa3e4217876a5174193f9e407bf87aef3bce2 100644 --- a/src/util/util_errors.c +++ b/src/util/util_errors.c @@ -56,6 +56,8 @@ struct err_string error_to_str[] = { { "No POSIX attributes detected" }, /* ERR_NO_POSIX */ { "Extra attribute is a duplicate" }, /* ERR_DUP_EXTRA_ATTR */ { "Malformed extra attribute" }, /* ERR_INVALID_EXTRA_ATTR */ + { "Cannot get bus message sender" }, /* ERR_SBUS_GET_SENDER_ERROR */ + { "Bus message has no sender" }, /* ERR_SBUS_NO_SENDER */ }; diff --git a/src/util/util_errors.h b/src/util/util_errors.h index f03fc16b1444d7e1370c22044b248585f076f32a..4d9f16c0aaea9128b8405048cb32a6d57850e1fc 100644 --- a/src/util/util_errors.h +++ b/src/util/util_errors.h @@ -78,6 +78,8 @@ enum sssd_errors { ERR_NO_POSIX, ERR_DUP_EXTRA_ATTR, ERR_INVALID_EXTRA_ATTR, + ERR_SBUS_GET_SENDER_ERROR, + ERR_SBUS_NO_SENDER, ERR_LAST /* ALWAYS LAST */ }; -- 1.9.0