From 64eafdf60552bbd7e22fb1c8fc8fc2b42a3cf68b Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Fri, 18 Sep 2020 16:37:50 -0400 Subject: [PATCH] trigger: expose trigger owner uid MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit To facilitate behavior management for the root user and to allow duplicate trigger names across users, enforce the usage of the trigger owner user id. The root user will be able to register and unregister triggers on behalf of other users. The root user will also have visibility on triggers of other users. Only the root user can use the `lttng_trigger_set_owner_uid` function successfully. As indicated in the comments, this function performs a client-side validation steps to catch mis-uses, but this is properly enforced on the sessiond's end in the register/unregister trigger commands. With the future addition of a trigger name (id), the owner id and the name will act as a key tuple allowing identicaly named triggers across users. We plan on exposing the `--user` switch in the upcoming command line (add-trigger, remove-trigger). Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: Ifca3c41b7ffd97b67e16fb80c18472b667cb2f56 --- include/lttng/trigger/trigger-internal.h | 11 +++- include/lttng/trigger/trigger.h | 34 ++++++++++ src/bin/lttng-sessiond/action-executor.c | 3 +- src/bin/lttng-sessiond/cmd.c | 32 ++++++++- src/common/trigger.c | 83 ++++++++++++++++++++++-- src/lib/lttng-ctl/lttng-ctl.c | 58 +++++++++++++++++ 6 files changed, 209 insertions(+), 12 deletions(-) diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h index 4b031137c..a554a31e5 100644 --- a/include/lttng/trigger/trigger-internal.h +++ b/include/lttng/trigger/trigger-internal.h @@ -26,12 +26,18 @@ struct lttng_trigger { struct lttng_condition *condition; struct lttng_action *action; - LTTNG_OPTIONAL(struct lttng_credentials) creds; + /* For now only the uid portion of the credentials is used. */ + struct lttng_credentials creds; }; struct lttng_trigger_comm { /* length excludes its own length. */ uint32_t length; + /* + * Credentials, only the uid portion is used for now. + * Used as an override when desired by the root user. + */ + uint64_t uid; /* A condition and action object follow. */ char payload[]; } LTTNG_PACKED; @@ -66,8 +72,7 @@ const struct lttng_credentials *lttng_trigger_get_credentials( const struct lttng_trigger *trigger); LTTNG_HIDDEN -void lttng_trigger_set_credentials( - struct lttng_trigger *trigger, +void lttng_trigger_set_credentials(struct lttng_trigger *trigger, const struct lttng_credentials *creds); #endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/include/lttng/trigger/trigger.h b/include/lttng/trigger/trigger.h index feffc6a8f..e92897fd5 100644 --- a/include/lttng/trigger/trigger.h +++ b/include/lttng/trigger/trigger.h @@ -8,6 +8,8 @@ #ifndef LTTNG_TRIGGER_H #define LTTNG_TRIGGER_H +#include + struct lttng_action; struct lttng_condition; struct lttng_trigger; @@ -21,6 +23,16 @@ enum lttng_register_trigger_status { LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1, }; +enum lttng_trigger_status { + LTTNG_TRIGGER_STATUS_OK = 0, + LTTNG_TRIGGER_STATUS_ERROR = -1, + LTTNG_TRIGGER_STATUS_UNKNOWN = -2, + LTTNG_TRIGGER_STATUS_INVALID = -3, + LTTNG_TRIGGER_STATUS_UNSET = -4, + LTTNG_TRIGGER_STATUS_UNSUPPORTED = -5, + LTTNG_TRIGGER_STATUS_PERMISSION_DENIED = -6, +}; + /* * Create a trigger object associating a condition and an action. * @@ -42,6 +54,28 @@ enum lttng_register_trigger_status { extern struct lttng_trigger *lttng_trigger_create( struct lttng_condition *condition, struct lttng_action *action); +/* + * Set the user identity (uid) of a trigger. + * + * Only available for the root user (uid 0). + * + * Returns LTTNG_TRIGGER_STATUS_OK on success, + * LTTNG_TRIGGER_STATUS_EPERM if not authorized, + * LTTNG_TRIGGER_STATUS_INVALID if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_set_owner_uid( + struct lttng_trigger *trigger, uid_t uid); + +/* + * Get the user identity (uid) of a trigger. + * + * Returns LTTNG_TRIGGER_STATUS_OK on success, + * LTTNG_TRIGGER_STATUS_UNSET if unset, + * LTTNG_TRIGGER_STATUS_INVALID if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_get_owner_uid( + const struct lttng_trigger *trigger, uid_t *uid); + /* * Get the condition of a trigger. * diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c index d1369375d..f982b7065 100644 --- a/src/bin/lttng-sessiond/action-executor.c +++ b/src/bin/lttng-sessiond/action-executor.c @@ -188,8 +188,7 @@ static int action_executor_notify_handler(struct action_executor *executor, work_item->evaluation, lttng_trigger_get_credentials(work_item->trigger), LTTNG_OPTIONAL_GET_PTR(work_item->object_creds), - client_handle_transmission_status, - executor); + client_handle_transmission_status, executor); } static int action_executor_start_session_handler(struct action_executor *executor, diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 9a832b455..d8d17b59c 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -4314,8 +4314,21 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, } } - /* Set the trigger credential */ - lttng_trigger_set_credentials(trigger, &cmd_creds); + + /* + * Validate the trigger credentials against the command credentials. + * Only the root user can register a trigger with non-matching + * credentials. + */ + if (!lttng_credentials_is_equal_uid( + lttng_trigger_get_credentials(trigger), + &cmd_creds)) { + if (lttng_credentials_get_uid(&cmd_creds) != 0) { + ERR("Trigger credentials do not match the command credentials"); + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + } /* Inform the notification thread */ ret = notification_thread_command_register_trigger(notification_thread, @@ -4385,7 +4398,20 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, } } - lttng_trigger_set_credentials(trigger, &cmd_creds); + /* + * Validate the trigger credentials against the command credentials. + * Only the root user can unregister a trigger with non-matching + * credentials. + */ + if (!lttng_credentials_is_equal_uid( + lttng_trigger_get_credentials(trigger), + &cmd_creds)) { + if (lttng_credentials_get_uid(&cmd_creds) != 0) { + ERR("Trigger credentials do not match the command credentials"); + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + } ret = notification_thread_command_unregister_trigger(notification_thread, trigger); diff --git a/src/common/trigger.c b/src/common/trigger.c index 2ef77b236..5ae84f3d9 100644 --- a/src/common/trigger.c +++ b/src/common/trigger.c @@ -25,6 +25,11 @@ bool lttng_trigger_validate(struct lttng_trigger *trigger) goto end; } + if (!trigger->creds.uid.is_set) { + valid = false; + goto end; + } + valid = lttng_condition_validate(trigger->condition) && lttng_action_validate(trigger->action); end: @@ -127,6 +132,10 @@ ssize_t lttng_trigger_create_from_payload( struct lttng_condition *condition = NULL; struct lttng_action *action = NULL; const struct lttng_trigger_comm *trigger_comm; + struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_UNSET, + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; if (!src_view || !trigger) { ret = -1; @@ -135,6 +144,16 @@ ssize_t lttng_trigger_create_from_payload( /* lttng_trigger_comm header */ trigger_comm = (typeof(trigger_comm)) src_view->buffer.data; + + /* Set the trigger's creds. */ + if (trigger_comm->uid > (uint64_t) ((uid_t) -1)) { + /* UID out of range for this platform. */ + ret = -1; + goto end; + } + + LTTNG_OPTIONAL_SET(&creds.uid, trigger_comm->uid); + offset += sizeof(*trigger_comm); { /* struct lttng_condition */ @@ -179,6 +198,8 @@ ssize_t lttng_trigger_create_from_payload( goto error; } + lttng_trigger_set_credentials(*trigger, &creds); + /* * The trigger object owns references to the action and condition * objects. @@ -210,6 +231,12 @@ int lttng_trigger_serialize(struct lttng_trigger *trigger, size_t header_offset, size_before_payload; struct lttng_trigger_comm trigger_comm = {}; struct lttng_trigger_comm *header; + const struct lttng_credentials *creds = NULL; + + creds = lttng_trigger_get_credentials(trigger); + assert(creds); + + trigger_comm.uid = LTTNG_OPTIONAL_GET(creds->uid); header_offset = payload->buffer.size; ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm, @@ -256,14 +283,62 @@ LTTNG_HIDDEN const struct lttng_credentials *lttng_trigger_get_credentials( const struct lttng_trigger *trigger) { - return LTTNG_OPTIONAL_GET_PTR(trigger->creds); + return &trigger->creds; } LTTNG_HIDDEN -void lttng_trigger_set_credentials( - struct lttng_trigger *trigger, +void lttng_trigger_set_credentials(struct lttng_trigger *trigger, const struct lttng_credentials *creds) { assert(creds); - LTTNG_OPTIONAL_SET(&trigger->creds, *creds); + trigger->creds = *creds; +} + +enum lttng_trigger_status lttng_trigger_set_owner_uid( + struct lttng_trigger *trigger, uid_t uid) +{ + enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; + const struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(uid), + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; + + if (!trigger) { + ret = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + /* Client-side validation only to report a clearer error. */ + if (geteuid() != 0) { + ret = LTTNG_TRIGGER_STATUS_PERMISSION_DENIED; + goto end; + } + + lttng_trigger_set_credentials(trigger, &creds); + +end: + return ret; +} + +enum lttng_trigger_status lttng_trigger_get_owner_uid( + const struct lttng_trigger *trigger, uid_t *uid) +{ + enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; + const struct lttng_credentials *creds = NULL; + + if (!trigger || !uid ) { + ret = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + if (!trigger->creds.uid.is_set ) { + ret = LTTNG_TRIGGER_STATUS_UNSET; + goto end; + } + + creds = lttng_trigger_get_credentials(trigger); + *uid = lttng_credentials_get_uid(creds); + +end: + return ret; } diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 60e820bd2..60dcdc7d3 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -2956,6 +2956,10 @@ int lttng_register_trigger(struct lttng_trigger *trigger) struct lttcomm_session_msg *message_lsm; struct lttng_payload message; struct lttng_payload reply; + const struct lttng_credentials user_creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(geteuid()), + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; lttng_payload_init(&message); lttng_payload_init(&reply); @@ -2965,6 +2969,31 @@ int lttng_register_trigger(struct lttng_trigger *trigger) goto end; } + if (!trigger->creds.uid.is_set) { + /* Use the client's credentials as the trigger credentials. */ + lttng_trigger_set_credentials(trigger, &user_creds); + } else { + /* + * Validate that either the current trigger credentials and the + * client credentials are identical or that the current user is + * root. The root user can register, unregister triggers for + * himself and other users. + * + * This check is also present on the sessiond side, using the + * credentials passed on the socket. These check are all + * "safety" checks. + */ + const struct lttng_credentials *trigger_creds = + lttng_trigger_get_credentials(trigger); + + if (!lttng_credentials_is_equal_uid(trigger_creds, &user_creds)) { + if (lttng_credentials_get_uid(&user_creds) != 0) { + ret = -LTTNG_ERR_EPERM; + goto end; + } + } + } + if (!lttng_trigger_validate(trigger)) { ret = -LTTNG_ERR_INVALID_TRIGGER; goto end; @@ -3017,6 +3046,10 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger) struct lttcomm_session_msg *message_lsm; struct lttng_payload message; struct lttng_payload reply; + const struct lttng_credentials user_creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(geteuid()), + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; lttng_payload_init(&message); lttng_payload_init(&reply); @@ -3026,6 +3059,31 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger) goto end; } + if (!trigger->creds.uid.is_set) { + /* Use the client's credentials as the trigger credentials. */ + lttng_trigger_set_credentials(trigger, &user_creds); + } else { + /* + * Validate that either the current trigger credentials and the + * client credentials are identical or that the current user is + * root. The root user can register, unregister triggers for + * himself and other users. + * + * This check is also present on the sessiond side, using the + * credentials passed on the socket. These check are all + * "safety" checks. + */ + const struct lttng_credentials *trigger_creds = + lttng_trigger_get_credentials(trigger); + + if (!lttng_credentials_is_equal_uid(trigger_creds, &user_creds)) { + if (lttng_credentials_get_uid(&user_creds) != 0) { + ret = -LTTNG_ERR_EPERM; + goto end; + } + } + } + if (!lttng_trigger_validate(trigger)) { ret = -LTTNG_ERR_INVALID_TRIGGER; goto end; -- 2.34.1