trigger: expose trigger owner uid
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Fri, 18 Sep 2020 20:37:50 +0000 (16:37 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Mon, 9 Nov 2020 19:59:43 +0000 (14:59 -0500)
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 <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Change-Id: Ifca3c41b7ffd97b67e16fb80c18472b667cb2f56

include/lttng/trigger/trigger-internal.h
include/lttng/trigger/trigger.h
src/bin/lttng-sessiond/action-executor.c
src/bin/lttng-sessiond/cmd.c
src/common/trigger.c
src/lib/lttng-ctl/lttng-ctl.c

index 4b031137c6473153fc005321bb4a226c4c51fb3f..a554a31e536131a16d48f4e00088297f6ffe69d9 100644 (file)
@@ -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 */
index feffc6a8f9fb0f1c05c8b1f19b9cb1f308988a7e..e92897fd541e79accfeac412393dd780e1014a4d 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef LTTNG_TRIGGER_H
 #define LTTNG_TRIGGER_H
 
+#include <sys/types.h>
+
 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.
  *
index d1369375d6f70d71244059040f64a1a6456db438..f982b7065e5b357c88b344e3e8805842f1779310 100644 (file)
@@ -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,
index 9a832b4558679f1a3586d578a9eca9a8b3e8980b..d8d17b59cba2b07956ff7e08bf13c685dfca37da 100644 (file)
@@ -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);
index 2ef77b2360a2bf8ab1ea80cdf513da2b9d46ce2b..5ae84f3d9e8f6ef28cb6323cd8b83c62101291f8 100644 (file)
@@ -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;
 }
index 60e820bd25e4a1089a0854b3bd771ce4f9a85bf6..60dcdc7d33872cbf190e79b1dfe9e3385f72e545 100644 (file)
@@ -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;
This page took 0.047002 seconds and 4 git commands to generate.