sessiond: notification: introduce the notion of 'active' client
[lttng-tools.git] / src / bin / lttng-sessiond / notification-thread-events.c
index f7dd90cbe0aec976925dd0215af89d8b81648b10..a2fa8a1ca854933bdf505508002b3d113f609ce4 100644 (file)
@@ -131,6 +131,7 @@ struct notification_client_list {
 };
 
 struct notification_client {
+       notification_client_id id;
        int socket;
        /* Client protocol version. */
        uint8_t major, minor;
@@ -148,7 +149,15 @@ struct notification_client {
         */
        struct cds_list_head condition_list;
        struct cds_lfht_node client_socket_ht_node;
+       struct cds_lfht_node client_id_ht_node;
        struct {
+               /*
+                * If a client's communication is inactive, it means a fatal
+                * error (either a protocol error or the socket API returned
+                * a fatal error). No further communication should be attempted;
+                * the client is queued for clean-up.
+                */
+               bool active;
                struct {
                        /*
                         * During the reception of a message, the reception
@@ -262,16 +271,25 @@ int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list,
 
 
 static
-int match_client(struct cds_lfht_node *node, const void *key)
+int match_client_socket(struct cds_lfht_node *node, const void *key)
 {
        /* This double-cast is intended to supress pointer-to-cast warning. */
-       int socket = (int) (intptr_t) key;
-       struct notification_client *client;
+       const int socket = (int) (intptr_t) key;
+       const struct notification_client *client = caa_container_of(node,
+                       struct notification_client, client_socket_ht_node);
 
-       client = caa_container_of(node, struct notification_client,
-                       client_socket_ht_node);
+       return client->socket == socket;
+}
+
+static
+int match_client_id(struct cds_lfht_node *node, const void *key)
+{
+       /* This double-cast is intended to supress pointer-to-cast warning. */
+       const notification_client_id id = *((notification_client_id *) key);
+       const struct notification_client *client = caa_container_of(
+                       node, struct notification_client, client_id_ht_node);
 
-       return !!(client->socket == socket);
+       return client->id == id;
 }
 
 static
@@ -475,6 +493,18 @@ unsigned long hash_channel_key(struct channel_key *key)
        return key_hash ^ domain_hash;
 }
 
+static
+unsigned long hash_client_socket(int socket)
+{
+       return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed);
+}
+
+static
+unsigned long hash_client_id(notification_client_id id)
+{
+       return hash_key_u64(&id, lttng_ht_seed);
+}
+
 /*
  * Get the type of object to which a given condition applies. Bindings let
  * the notification system evaluate a trigger's condition when a given
@@ -1138,6 +1168,7 @@ void notification_client_destroy(struct notification_client *client,
 
        if (client->socket >= 0) {
                (void) lttcomm_close_unix_sock(client->socket);
+               client->socket = -1;
        }
        lttng_dynamic_buffer_reset(&client->communication.inbound.buffer);
        lttng_dynamic_buffer_reset(&client->communication.outbound.buffer);
@@ -1157,8 +1188,8 @@ struct notification_client *get_client_from_socket(int socket,
        struct notification_client *client = NULL;
 
        cds_lfht_lookup(state->client_socket_ht,
-                       hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed),
-                       match_client,
+                       hash_client_socket(socket),
+                       match_client_socket,
                        (void *) (unsigned long) socket,
                        &iter);
        node = cds_lfht_iter_get_node(&iter);
@@ -1172,6 +1203,34 @@ end:
        return client;
 }
 
+/*
+ * Call with rcu_read_lock held (and hold for the lifetime of the returned
+ * client pointer).
+ */
+static
+struct notification_client *get_client_from_id(notification_client_id id,
+               struct notification_thread_state *state)
+{
+       struct cds_lfht_iter iter;
+       struct cds_lfht_node *node;
+       struct notification_client *client = NULL;
+
+       cds_lfht_lookup(state->client_id_ht,
+                       hash_client_id(id),
+                       match_client_id,
+                       &id,
+                       &iter);
+       node = cds_lfht_iter_get_node(&iter);
+       if (!node) {
+               goto end;
+       }
+
+       client = caa_container_of(node, struct notification_client,
+                       client_id_ht_node);
+end:
+       return client;
+}
+
 static
 bool buffer_usage_condition_applies_to_channel(
                const struct lttng_condition *condition,
@@ -2146,10 +2205,6 @@ error_free_ht_element:
        free(trigger_ht_element);
 error:
        if (free_trigger) {
-               struct lttng_action *action = lttng_trigger_get_action(trigger);
-
-               lttng_condition_destroy(condition);
-               lttng_action_destroy(action);
                lttng_trigger_destroy(trigger);
        }
        rcu_read_unlock();
@@ -2184,7 +2239,6 @@ int handle_notification_thread_command_unregister_trigger(
        struct lttng_trigger_ht_element *trigger_ht_element = NULL;
        struct lttng_condition *condition = lttng_trigger_get_condition(
                        trigger);
-       struct lttng_action *action;
        enum lttng_error_code cmd_reply;
 
        rcu_read_lock();
@@ -2246,10 +2300,7 @@ int handle_notification_thread_command_unregister_trigger(
                        struct lttng_trigger_ht_element, node);
        cds_lfht_del(state->triggers_ht, triggers_ht_node);
 
-       condition = lttng_trigger_get_condition(trigger_ht_element->trigger);
-       lttng_condition_destroy(condition);
-       action = lttng_trigger_get_action(trigger_ht_element->trigger);
-       lttng_action_destroy(action);
+       /* Release the ownership of the trigger. */
        lttng_trigger_destroy(trigger_ht_element->trigger);
        call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu);
 end:
@@ -2355,12 +2406,6 @@ error:
        return -1;
 }
 
-static
-unsigned long hash_client_socket(int socket)
-{
-       return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed);
-}
-
 static
 int socket_set_non_blocking(int socket)
 {
@@ -2419,6 +2464,7 @@ int handle_notification_thread_client_connect(
                ret = -1;
                goto error;
        }
+       client->id = state->next_notification_client_id++;
        CDS_INIT_LIST_HEAD(&client->condition_list);
        lttng_dynamic_buffer_init(&client->communication.inbound.buffer);
        lttng_dynamic_buffer_init(&client->communication.outbound.buffer);
@@ -2467,6 +2513,9 @@ int handle_notification_thread_client_connect(
        cds_lfht_add(state->client_socket_ht,
                        hash_client_socket(client->socket),
                        &client->client_socket_ht_node);
+       cds_lfht_add(state->client_id_ht,
+                       hash_client_id(client->id),
+                       &client->client_id_ht_node);
        rcu_read_unlock();
 
        return ret;
@@ -2500,6 +2549,8 @@ int handle_notification_thread_client_disconnect(
        }
        cds_lfht_del(state->client_socket_ht,
                        &client->client_socket_ht_node);
+        cds_lfht_del(state->client_id_ht,
+                       &client->client_id_ht_node);
        notification_client_destroy(client, state);
 end:
        rcu_read_unlock();
@@ -2792,6 +2843,7 @@ int client_dispatch_message(struct notification_client *client,
                        goto end;
                }
                client->validated = true;
+               client->communication.active = true;
                break;
        }
        case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE:
@@ -3102,12 +3154,45 @@ int client_enqueue_dropped_notification(struct notification_client *client)
        return ret;
 }
 
+/*
+ * Permission checks relative to notification channel clients are performed
+ * here. Notice how object, client, and trigger credentials are involved in
+ * this check.
+ *
+ * The `object` credentials are the credentials associated with the "subject"
+ * of a condition. For instance, a `rotation completed` condition applies
+ * to a session. When that condition is met, it will produce an evaluation
+ * against a session. Hence, in this case, the `object` credentials are the
+ * credentials of the "subject" session.
+ *
+ * The `trigger` credentials are the credentials of the user that registered the
+ * trigger.
+ *
+ * The `client` credentials are the credentials of the user that created a given
+ * notification channel.
+ *
+ * In terms of visibility, it is expected that non-privilieged users can only
+ * register triggers against "their" objects (their own sessions and
+ * applications they are allowed to interact with). They can then open a
+ * notification channel and subscribe to notifications associated with those
+ * triggers.
+ *
+ * As for privilieged users, they can register triggers against the objects of
+ * other users. They can then subscribe to the notifications associated to their
+ * triggers. Privilieged users _can't_ subscribe to the notifications of
+ * triggers owned by other users; they must create their own triggers.
+ *
+ * This is more a concern of usability than security. It would be difficult for
+ * a root user reliably subscribe to a specific set of conditions without
+ * interference from external users (those could, for instance, unregister
+ * their triggers).
+ */
 static
 int send_evaluation_to_clients(const struct lttng_trigger *trigger,
                const struct lttng_evaluation *evaluation,
                struct notification_client_list* client_list,
                struct notification_thread_state *state,
-               uid_t channel_uid, gid_t channel_gid)
+               uid_t object_uid, gid_t object_gid)
 {
        int ret = 0;
        struct lttng_payload msg_payload;
@@ -3119,6 +3204,7 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger,
        struct lttng_notification_channel_message msg_header = {
                .type = (int8_t) LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION,
        };
+       const struct lttng_credentials *trigger_creds = lttng_trigger_get_credentials(trigger);
 
        lttng_payload_init(&msg_payload);
 
@@ -3144,10 +3230,15 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger,
                struct notification_client *client =
                                client_list_element->client;
 
-               if (client->uid != channel_uid && client->gid != channel_gid &&
+               if (client->uid != object_uid && client->gid != object_gid &&
                                client->uid != 0) {
                        /* Client is not allowed to monitor this channel. */
-                       DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this channel");
+                       DBG("[notification-thread] Skipping client at it does not have the object permission to receive notification for this trigger");
+                       continue;
+               }
+
+               if (client->uid != trigger_creds->uid && client->gid != trigger_creds->gid) {
+                       DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger");
                        continue;
                }
 
This page took 0.02691 seconds and 4 git commands to generate.