static void process_open (CockpitRouter *self, CockpitTransport *transport, const gchar *channel, JsonObject *options, GBytes *data) { GList *l; GBytes *new_payload = NULL; if (!channel) { g_warning ("Caller tried to open channel with invalid id"); cockpit_transport_close (transport, "protocol-error"); } /* Check that this isn't a local channel */ else if (g_hash_table_lookup (self->channels, channel)) { g_warning ("%s: caller tried to reuse a channel that's already in use", channel); cockpit_transport_close (self->transport, "protocol-error"); return; } /* Request that this channel is frozen, and requeue its open message for later */ else if (g_hash_table_size (self->fences) > 0 && !g_hash_table_lookup (self->fences, channel)) { if (!self->fenced) self->fenced = g_queue_new (); g_queue_push_tail (self->fenced, g_strdup (channel)); cockpit_transport_freeze (self->transport, channel); cockpit_transport_emit_control (self->transport, "open", channel, options, data); } else if (!cockpit_router_normalize_host (self, options)) { g_warning ("%s: caller specified invalid 'host' field in open message", channel); process_open_not_supported (self, channel, options, data, NULL); } /* Now go throgh the rules */ else { cockpit_router_normalize_host_params (options); new_payload = cockpit_json_write_bytes (options); for (l = self->rules; l != NULL; l = g_list_next (l)) { if (router_rule_match (l->data, options) && router_rule_invoke (l->data, self, channel, options, new_payload)) { break; } } } if (new_payload) g_bytes_unref (new_payload); }
static void process_open (CockpitTransport *transport, const gchar *channel_id, JsonObject *options) { CockpitChannel *channel; GType channel_type; const gchar *payload; gint i; if (!channel_id) { g_warning ("Caller tried to open channel with invalid id"); cockpit_transport_close (transport, "protocol-error"); } else if (g_hash_table_lookup (channels, channel_id)) { g_warning ("Caller tried to reuse a channel that's already in use"); cockpit_transport_close (transport, "protocol-error"); } else { if (!cockpit_json_get_string (options, "payload", NULL, &payload)) payload = NULL; /* This will close with "not-supported" */ channel_type = COCKPIT_TYPE_CHANNEL; for (i = 0; payload_types[i].name != NULL; i++) { if (g_strcmp0 (payload, payload_types[i].name) == 0) { channel_type = payload_types[i].function(); break; } } channel = g_object_new (channel_type, "transport", transport, "id", channel_id, "options", options, NULL); g_hash_table_insert (channels, g_strdup (channel_id), channel); g_signal_connect (channel, "closed", G_CALLBACK (on_channel_closed), NULL); } }
static gboolean on_transport_control (CockpitTransport *transport, const char *command, const gchar *channel_id, JsonObject *options, GBytes *message, gpointer user_data) { if (g_str_equal (command, "init")) { process_init (transport, options); return TRUE; } else if (!init_received) { g_warning ("caller did not send 'init' message first"); cockpit_transport_close (transport, "protocol-error"); return TRUE; } else if (g_str_equal (command, "open")) { process_open (transport, channel_id, options); return TRUE; } return FALSE; }
static gboolean cockpit_transport_default_recv (CockpitTransport *transport, const gchar *channel, GBytes *payload) { gboolean ret = FALSE; const gchar *inner_channel; JsonObject *options; const gchar *command = NULL; /* Our default handler parses control channel and fires control signal */ if (channel) return FALSE; /* Read out the actual command and channel this message is about */ if (!cockpit_transport_parse_command (payload, &command, &inner_channel, &options)) { /* Warning already logged */ cockpit_transport_close (transport, "protocol-error"); return TRUE; } g_signal_emit (transport, signals[CONTROL], 0, command, inner_channel, options, payload, &ret); if (!ret) g_debug ("received unknown control command: %s", command); json_object_unref (options); return TRUE; }
static gboolean on_transport_control (CockpitTransport *transport, const char *command, const gchar *channel_id, JsonObject *options, GBytes *message, gpointer user_data) { if (g_str_equal (command, "init")) { process_init (transport, options); return TRUE; } if (!init_received) { g_warning ("caller did not send 'init' message first"); cockpit_transport_close (transport, "protocol-error"); return TRUE; } if (g_str_equal (command, "open")) { process_open (transport, channel_id, options); return TRUE; } else if (g_str_equal (command, "close")) { if (!channel_id) { g_warning ("Caller tried to close channel without an id"); cockpit_transport_close (transport, "protocol-error"); } else { /* * The channel may no longer exist due to a race of the bridge closing * a channel and the web closing it at the same time. */ g_debug ("already closed channel %s", channel_id); } } return FALSE; }
static void process_open (CockpitTransport *transport, const gchar *channel_id, JsonObject *options) { CockpitChannel *channel; GType channel_type; const gchar *payload; const gchar *internal; if (!channel_id) { g_warning ("Caller tried to open channel with invalid id"); cockpit_transport_close (transport, "protocol-error"); } else if (g_hash_table_lookup (channels, channel_id)) { g_warning ("Caller tried to reuse a channel that's already in use"); cockpit_transport_close (transport, "protocol-error"); } else { if (!cockpit_json_get_string (options, "payload", NULL, &payload)) payload = NULL; if (!cockpit_json_get_string (options, "internal", NULL, &internal)) internal = NULL; /* This will close with "not-supported" */ channel_type = COCKPIT_TYPE_CHANNEL; if (g_strcmp0 (payload, "stream") == 0 && g_strcmp0 (internal, "ssh-agent") == 0) channel_type = COCKPIT_TYPE_PIPE_CHANNEL; channel = g_object_new (channel_type, "transport", transport, "id", channel_id, "options", options, NULL); g_hash_table_insert (channels, g_strdup (channel_id), channel); g_signal_connect (channel, "closed", G_CALLBACK (on_channel_closed), NULL); } }
static void outbound_protocol_error (CockpitWebService *self, CockpitTransport *transport, const gchar *problem) { if (problem == NULL) problem = "protocol-error"; cockpit_transport_close (transport, problem); }
static gboolean on_transport_control (CockpitTransport *transport, const char *command, const gchar *channel_id, JsonObject *options, GBytes *message, gpointer user_data) { CockpitRouter *self = user_data; if (g_str_equal (command, "init")) { process_init (self, transport, options); return TRUE; } if (!self->init_host) { g_warning ("caller did not send 'init' message first"); cockpit_transport_close (transport, "protocol-error"); return TRUE; } if (g_str_equal (command, "open")) { process_open (self, transport, channel_id, options, message); return TRUE; } else if (g_str_equal (command, "kill")) { process_kill (self, options); return TRUE; } else if (g_str_equal (command, "close")) { if (!channel_id) { g_warning ("Caller tried to close channel without an id"); cockpit_transport_close (transport, "protocol-error"); } } return FALSE; }
static void process_init (CockpitRouter *self, CockpitTransport *transport, JsonObject *options) { const gchar *problem = NULL; const gchar *host; gint64 version = -1; if (self->init_host) { g_warning ("caller already sent another 'init' message"); problem = "protocol-error"; } else if (!cockpit_json_get_int (options, "version", -1, &version)) { g_warning ("invalid 'version' field in init message"); problem = "protocol-error"; } else if (version == -1) { g_warning ("missing 'version' field in init message"); problem = "protocol-error"; } else if (!cockpit_json_get_string (options, "host", NULL, &host)) { g_warning ("invalid 'host' field in init message"); problem = "protocol-error"; } else if (host == NULL) { g_message ("missing 'host' field in init message"); problem = "protocol-error"; } else if (version != 1) { g_message ("unsupported 'version' of cockpit protocol: %" G_GINT64_FORMAT, version); problem = "not-supported"; } if (problem) { cockpit_transport_close (transport, problem); } else { g_debug ("received init message"); g_assert (host != NULL); self->init_host = g_strdup (host); problem = NULL; } }
static void process_init (CockpitTransport *transport, JsonObject *options) { gint64 version = -1; if (!cockpit_json_get_int (options, "version", -1, &version)) { g_warning ("invalid version field in init message"); cockpit_transport_close (transport, "protocol-error"); } if (version == 1) { g_debug ("received init message"); init_received = TRUE; } else { g_message ("unsupported version of cockpit protocol: %" G_GINT64_FORMAT, version); cockpit_transport_close (transport, "not-supported"); } }
static gboolean on_transport_control (CockpitTransport *transport, const char *command, guint channel, JsonObject *options, GBytes *message, gpointer user_data) { CockpitPolkitAgent *self = COCKPIT_POLKIT_AGENT (user_data); ReauthorizeCaller *caller; const gchar *response; const gchar *cookie; GBytes *bytes; if (!g_str_equal (command, "authorize")) return FALSE; if (!cockpit_json_get_string (options, "cookie", NULL, &cookie) || !cockpit_json_get_string (options, "response", NULL, &response) || !cookie || !response) { g_warning ("got an invalid authorize command from cockpit-ws"); cockpit_transport_close (transport, "protocol-error"); return TRUE; } caller = g_hash_table_lookup (self->callers, cookie); if (!caller) { g_debug ("received authorize response for caller that has gone away"); return TRUE; } g_debug ("got \"authorize\" response from cockpit-ws, will send to helper: %s", response); bytes = g_bytes_new_with_free_func (response, strlen (response), (GDestroyNotify)json_object_unref, json_object_ref (options)); cockpit_pipe_write (caller->helper, bytes); g_bytes_unref (bytes); bytes = g_bytes_new_static ("\n", 1); cockpit_pipe_write (caller->helper, bytes); g_bytes_unref (bytes); return TRUE; }
static void cockpit_web_service_dispose (GObject *object) { CockpitWebService *self = COCKPIT_WEB_SERVICE (object); gboolean emit = FALSE; if (self->control_sig) g_signal_handler_disconnect (self->transport, self->control_sig); self->control_sig = 0; if (self->recv_sig) g_signal_handler_disconnect (self->transport, self->recv_sig); self->recv_sig = 0; if (self->closed_sig) g_signal_handler_disconnect (self->transport, self->closed_sig); self->closed_sig = 0; if (!self->sent_done) { self->sent_done = TRUE; cockpit_transport_close (self->transport, NULL); } if (!self->closing) { g_debug ("web service closing"); emit = TRUE; } self->closing = TRUE; cockpit_sockets_close (&self->sockets, NULL); if (emit) g_signal_emit (self, sig_destroy, 0); G_OBJECT_CLASS (cockpit_web_service_parent_class)->dispose (object); }