static void cockpit_fswrite_prepare (CockpitChannel *channel) { CockpitFswrite *self = COCKPIT_FSWRITE (channel); const gchar *problem = "protocol-error"; JsonObject *options; gchar *actual_tag = NULL; COCKPIT_CHANNEL_CLASS (cockpit_fswrite_parent_class)->prepare (channel); options = cockpit_channel_get_options (channel); if (!cockpit_json_get_string (options, "path", NULL, &self->path)) { g_warning ("invalid \"path\" option for fswrite1 channel"); goto out; } else if (self->path == NULL || g_str_equal (self->path, "")) { g_warning ("missing \"path\" option for fswrite1 channel"); goto out; } if (!cockpit_json_get_string (options, "tag", NULL, &self->expected_tag)) { g_warning ("%s: invalid \"tag\" option for fswrite1 channel", self->path); goto out; } actual_tag = cockpit_get_file_tag (self->path); if (self->expected_tag && g_strcmp0 (self->expected_tag, actual_tag)) { problem = "change-conflict"; goto out; } // TODO - delay the opening until the first content message. That // way, we don't create a useless temporary file (which might even // fail). for (int i = 1; i < 10000; i++) { self->tmp_path = g_strdup_printf ("%s.%d", self->path, i); self->fd = open (self->tmp_path, O_WRONLY | O_CREAT | O_EXCL, 0666); if (self->fd >= 0 || errno != EEXIST) break; g_free (self->tmp_path); self->tmp_path = NULL; } problem = NULL; if (self->fd < 0) close_with_errno (self, "couldn't open unique file", errno); else cockpit_channel_ready (channel); out: g_free (actual_tag); if (problem) cockpit_channel_close (channel, problem); }
static gboolean pcp_filter (CockpitPortal *self, const gchar *command, const gchar *channel, JsonObject *options, GBytes *payload) { const gchar *type; const gchar *source; if (g_str_equal (command, "open") && channel) { if (!cockpit_json_get_string (options, "payload", NULL, &type)) type = NULL; if (!cockpit_json_get_string (options, "source", NULL, &source)) source = NULL; if (g_strcmp0 (type, "metrics1") != 0 || g_strcmp0 (source, "internal") == 0) { return FALSE; } g_debug ("pcp portal channel: %s", channel); cockpit_portal_add_channel (self, channel, COCKPIT_PORTAL_NORMAL); return TRUE; } return FALSE; }
/* * For backwards compatibility we need to normalize some host params * so they can be matched against. * * Some sessions shouldn't be shared by multiple channels, such as those that * explicitly specify a host-key or specific user. This changed over time * so modify things to make it a simple match. * * If the given user is the current user, remove it. Preserves the current * behavior. * */ static void cockpit_router_normalize_host_params (JsonObject *options) { const gchar *sharable = NULL; const gchar *user = NULL; gboolean needs_private = FALSE; if (!cockpit_json_get_string (options, "session", NULL, &sharable)) sharable = NULL; if (!cockpit_json_get_string (options, "user", NULL, &user)) user = NULL; if (!sharable) { /* Fallback to older ways of indicating this */ if (user || json_object_has_member (options, "host-key")) needs_private = TRUE; if (json_object_has_member (options, "temp-session")) { if (needs_private && !cockpit_json_get_bool (options, "temp-session", TRUE, &needs_private)) needs_private = TRUE; json_object_remove_member (options, "temp-session"); } } if (g_strcmp0 (user, g_get_user_name ()) == 0) json_object_remove_member (options, "user"); if (needs_private) json_object_set_string_member (options, "session", "private"); }
static gboolean convert_metric_description (CockpitInternalMetrics *self, JsonNode *node, MetricInfo *info, int index) { const gchar *name; const gchar *units; if (json_node_get_node_type (node) == JSON_NODE_OBJECT) { if (!cockpit_json_get_string (json_node_get_object (node), "name", NULL, &name) || name == NULL) { g_warning ("invalid \"metrics\" option was specified (no name for metric %d)", index); return FALSE; } if (!cockpit_json_get_string (json_node_get_object (node), "units", NULL, &units)) { g_warning ("invalid units for metric %s (not a string)", name); return FALSE; } if (!cockpit_json_get_string (json_node_get_object (node), "derive", NULL, &info->derive)) { g_warning ("invalid derivation mode for metric %s (not a string)", name); return FALSE; } } else { g_warning ("invalid \"metrics\" option was specified (not an object for metric %d)", index); return FALSE; } MetricDescription *desc = find_metric_description (name); if (desc == NULL) { g_message ("unknown internal metric %s", name); } else { if (units && g_strcmp0 (desc->units, units) != 0) { g_warning ("%s has units %s, not %s", name, desc->units, units); return FALSE; } if (desc->instanced) info->instances = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); info->desc = desc; self->samplers |= desc->sampler; } return TRUE; }
static CockpitCreds * parse_ssh_spawn_results (CockpitAuth *self, AuthData *ad, GHashTable *headers, JsonObject **prompt_data, GError **error) { CockpitCreds *creds = NULL; JsonObject *results = NULL; JsonObject *auth_results = NULL; const gchar *pw_result = NULL; const gchar *user; const gchar *error_str; results = cockpit_auth_process_parse_result (ad->auth_process, ad->response_data, error); if (results) { user = cockpit_auth_process_get_authenticated_user (ad->auth_process, results, prompt_data, error); if (user) { creds = create_creds_for_spawn_authenticated (self, user, ad, results, ad->response_data); } else if (cockpit_json_get_string (results, "error", NULL, &error_str)) { if (g_strcmp0 (error_str, "authentication-failed") == 0) { cockpit_json_get_object (results, "auth-method-results", NULL, &auth_results); if (auth_results) cockpit_json_get_string (auth_results, "password", NULL, &pw_result); if (!pw_result || g_strcmp0 (pw_result, "no-server-support") == 0) { g_clear_error (error); g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED, "Authentication failed: authentication-not-supported"); } } else if (g_strcmp0 (error_str, "terminated") == 0) { g_clear_error (error); g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED, "Authentication failed: terminated"); } } json_object_unref (results); } return creds; }
static void on_auth_process_message (CockpitAuthProcess *auth_process, GBytes *bytes, gpointer user_data) { CockpitSshTransport *self = COCKPIT_SSH_TRANSPORT (user_data); JsonObject *json = NULL; gchar *response = NULL; GError *error = NULL; gsize len; gboolean prompt_claimed; gboolean final = TRUE; GBytes *blank = NULL; const gchar *user; const gchar *error_str; const gchar *prompt; const gchar *message; const gchar *host_key = NULL; const gchar *host_fp = NULL; JsonObject *auth_result = NULL; const gchar *problem = "internal-error"; len = g_bytes_get_size (bytes); response = g_strndup (g_bytes_get_data (bytes, NULL), len); json = cockpit_auth_process_parse_result (self->auth_process, response, &error); if (json) { if (!cockpit_json_get_string (json, "error", NULL, &error_str) || !cockpit_json_get_string (json, "message", NULL, &message) || !cockpit_json_get_string (json, "prompt", NULL, &prompt) || !cockpit_json_get_string (json, "user", NULL, &user)) { g_warning ("%s: got invalid authentication json", self->logname); } else if (error_str) { problem = error_str; g_debug ("%s: got authentication error %s: %s", self->logname, error_str, message); } else if (prompt) { final = FALSE; problem = NULL; // Send the signal, if nothing handles it write a blank response. g_signal_emit (self, signals[PROMPT], 0, json, &prompt_claimed); if (!prompt_claimed) { blank = g_bytes_new_static ("", 0); cockpit_auth_process_write_auth_bytes (self->auth_process, blank); g_bytes_unref (blank); } } else if (user)
static gboolean parse_option_file_or_data (CockpitChannel *self, JsonObject *options, const gchar *option, const gchar **file, const gchar **data) { JsonObject *object; JsonNode *node; g_assert (file != NULL); g_assert (data != NULL); node = json_object_get_member (options, option); if (!node) { *file = NULL; *data = NULL; return TRUE; } if (!JSON_NODE_HOLDS_OBJECT (node)) { cockpit_channel_fail (self, "protocol-error", "invalid \"%s\" tls option for channel", option); return FALSE; } object = json_node_get_object (node); if (!cockpit_json_get_string (object, "file", NULL, file)) { cockpit_channel_fail (self, "protocol-error", "invalid \"file\" %s option for channel", option); } else if (!cockpit_json_get_string (object, "data", NULL, data)) { cockpit_channel_fail (self, "protocol-error", "invalid \"data\" %s option for channel", option); } else if (!*file && !*data) { cockpit_channel_fail (self, "not-supported", "missing or unsupported \"%s\" option for channel", option); } else if (*file && *data) { cockpit_channel_fail (self, "protocol-error", "cannot specify both \"file\" and \"data\" in \"%s\" option for channel", option); } else { return TRUE; } return FALSE; }
/** * cockpit_transport_parse_command: * @payload: command JSON payload to parse * @command: a location to return the command * @channel: location to return the channel * @options: location to return the options * * Parse a command and return various values from the * command. The @options value is transfered with ownership, * so you should free it after done. @command and @channel are owned by * @options. @channel will be NULL for a missing channel. * * On failure, message has already been printed. * * Returns: whether command parsed or not. */ gboolean cockpit_transport_parse_command (GBytes *payload, const gchar **command, const gchar **channel, JsonObject **options) { GError *error = NULL; gboolean ret = FALSE; JsonObject *object; gboolean valid; object = cockpit_json_parse_bytes (payload, &error); if (!object) { g_warning ("Received unparseable control message: %s", error->message); g_error_free (error); goto out; } /* Parse out the command */ if (command) { if (!cockpit_json_get_string (object, "command", NULL, command) || *command == NULL || g_str_equal (*command, "")) { g_warning ("Received invalid control message: invalid or missing command"); goto out; } } /* Parse out the channel */ valid = cockpit_json_get_string (object, "channel", NULL, channel); if (valid && *channel) { valid = (!g_str_equal ("", *channel) && strcspn (*channel, "\n") == strlen (*channel)); } if (!valid) { g_warning ("Received invalid control message: invalid channel"); goto out; } *options = json_object_ref (object); ret = TRUE; out: if (object) json_object_unref (object); return ret; }
static const gchar * parse_option_file_or_data (JsonObject *options, const gchar *option, const gchar **file, const gchar **data) { JsonObject *object; JsonNode *node; g_assert (file != NULL); g_assert (data != NULL); node = json_object_get_member (options, option); if (!node) { *file = NULL; *data = NULL; return NULL; } if (!JSON_NODE_HOLDS_OBJECT (node)) { g_warning ("invalid \"%s\" tls option for channel", option); return "protocol-error"; } object = json_node_get_object (node); if (!cockpit_json_get_string (object, "file", NULL, file)) { g_warning ("invalid \"file\" %s option for channel", option); return "protocol-error"; } else if (!cockpit_json_get_string (object, "data", NULL, data)) { g_warning ("invalid \"data\" %s option for channel", option); return "protocol-error"; } else if (!*file && !*data) { g_warning ("missing or unsupported \"%s\" option for channel", option); return "not-supported"; } else if (*file && *data) { g_warning ("cannot specify both \"file\" and \"data\" in \"%s\" option for channel", option); return "protocol-error"; } return NULL; }
static void cockpit_channel_real_prepare (CockpitChannel *channel) { CockpitChannel *self = COCKPIT_CHANNEL (channel); JsonObject *options; const gchar *binary; const gchar *payload; options = cockpit_channel_get_options (self); if (!cockpit_channel_ensure_capable (self, options)) return; if (G_OBJECT_TYPE (channel) == COCKPIT_TYPE_CHANNEL) { if (!cockpit_json_get_string (options, "payload", NULL, &payload)) payload = NULL; if (payload) { g_warning ("bridge doesn't support payloads of type: %s", payload); cockpit_channel_close (channel, "not-supported"); } else { g_warning ("no payload type present in request to open channel"); cockpit_channel_close (channel, "protocol-error"); } return; } if (!cockpit_json_get_string (options, "binary", NULL, &binary)) { g_warning ("%s: channel has invalid \"binary\" option", self->priv->id); cockpit_channel_close (self, "protocol-error"); } else if (binary != NULL) { self->priv->binary_ok = TRUE; if (g_str_equal (binary, "base64")) { self->priv->base64_encoding = TRUE; } else if (!g_str_equal (binary, "raw")) { g_warning ("%s: channel has invalid \"binary\" option: %s", self->priv->id, binary); cockpit_channel_close (self, "protocol-error"); } } }
static gboolean router_rule_match (RouterRule *rule, JsonObject *object) { RouterMatch *match; const gchar *value; JsonNode *node; guint i; for (i = 0; rule->matches && rule->matches[i].name != NULL; i++) { match = &rule->matches[i]; if (match->glob) { if (!cockpit_json_get_string (object, match->name, NULL, &value) || !value || !g_pattern_match (match->glob, strlen (value), value, NULL)) return FALSE; } else if (match->node) { node = json_object_get_member (object, match->name); if (!node || !cockpit_json_equal (match->node, node)) return FALSE; } else { if (!json_object_has_member (object, match->name)) return FALSE; } } return TRUE; }
static void process_control (CockpitChannel *self, const gchar *command, JsonObject *options) { CockpitChannelClass *klass; const gchar *problem; if (g_str_equal (command, "close")) { g_debug ("close channel %s", self->priv->id); if (!cockpit_json_get_string (options, "problem", NULL, &problem)) problem = NULL; cockpit_channel_close (self, problem); return; } if (g_str_equal (command, "done")) { if (self->priv->received_done) cockpit_channel_fail (self, "protocol-error", "channel received second done"); else self->priv->received_done = TRUE; } klass = COCKPIT_CHANNEL_GET_CLASS (self); if (klass->control) (klass->control) (self, command, options); }
static void cockpit_channel_real_prepare (CockpitChannel *channel) { CockpitChannel *self = COCKPIT_CHANNEL (channel); JsonObject *options; const gchar *binary; options = cockpit_channel_get_options (self); if (!cockpit_channel_ensure_capable (self, options)) return; if (G_OBJECT_TYPE (channel) == COCKPIT_TYPE_CHANNEL) { cockpit_channel_close (channel, "not-supported"); return; } if (!cockpit_json_get_string (options, "binary", NULL, &binary)) { cockpit_channel_fail (self, "protocol-error", "channel has invalid \"binary\" option"); } else if (binary != NULL) { self->priv->binary_ok = TRUE; if (!g_str_equal (binary, "raw")) { cockpit_channel_fail (self, "protocol-error", "channel has invalid \"binary\" option: %s", binary); } } }
static gboolean on_transport_control (CockpitTransport *transport, const char *command, const gchar *channel, JsonObject *options, GBytes *payload, CockpitChannelSocket *chock) { const gchar *problem; if (channel && g_str_equal (channel, chock->channel)) { if (g_str_equal (command, "close")) { if (!cockpit_json_get_string (options, "problem", NULL, &problem)) problem = NULL; cockpit_channel_socket_close (chock, problem); } /* Any other control message for this channel is discarded */ return TRUE; } return FALSE; }
static gboolean parse_httpstream_response (CockpitChannelResponse *chesp, JsonObject *object, gint64 *status, const gchar **reason) { JsonNode *node; if (!cockpit_json_get_int (object, "status", 200, status) || !cockpit_json_get_string (object, "reason", NULL, reason)) { g_warning ("%s: received invalid httpstream response", chesp->logname); return FALSE; } node = json_object_get_member (object, "headers"); if (node) { if (!JSON_NODE_HOLDS_OBJECT (node)) { g_warning ("%s: received invalid httpstream headers", chesp->logname); return FALSE; } json_object_foreach_member (json_node_get_object (node), object_to_headers, chesp->headers); } return TRUE; }
static gboolean on_transport_control (CockpitTransport *transport, const gchar *command, const gchar *channel, JsonObject *options, GBytes *message, CockpitChannelResponse *chesp) { const gchar *problem = NULL; if (!channel || !g_str_equal (channel, chesp->channel)) return FALSE; /* not handled */ if (g_str_equal (command, "done")) { ensure_headers (chesp, 200, "OK"); cockpit_web_response_complete (chesp->response); return TRUE; } else if (g_str_equal (command, "close")) { if (!cockpit_json_get_string (options, "problem", NULL, &problem)) { g_message ("%s: received close command with invalid problem", chesp->logname); problem = "disconnected"; } cockpit_channel_response_close (chesp, problem); } else { /* Ignore other control messages */ } return TRUE; /* handled */ }
static gboolean process_open_channel (CockpitRouter *self, const gchar *channel, JsonObject *options, GBytes *data, gpointer user_data) { GType (* type_function) (void) = user_data; GType channel_type = 0; const gchar *group; if (!cockpit_json_get_string (options, "group", "default", &group)) g_warning ("%s: caller specified invalid 'group' field in open message", channel); g_assert (type_function != NULL); channel_type = type_function (); if (g_str_equal (group, "fence")) g_hash_table_add (self->fences, g_strdup (channel)); g_hash_table_insert (self->groups, g_strdup (channel), g_strdup (group)); create_channel (self, channel, options, channel_type); return TRUE; }
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 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); } }
/** * cockpit_channel_get_option: * @self: a channel * @name: the option name * * Called by implementations to get a string value from the * channel's options. * * Returns: (transfer none): the option value or NULL */ const gchar * cockpit_channel_get_option (CockpitChannel *self, const gchar *name) { const gchar *value; if (!cockpit_json_get_string (self->priv->open_options, name, NULL, &value)) value = NULL; return value; }
/** * cockpit_channel_control: * @self: the channel * @command: the control command * @options: optional control message or NULL * * Send a control message to the other side. * * If @options is not NULL, then it may be modified by this code. * * With @command of "done" will send an EOF to the other side. This * should only be called once. Whether an EOF should be sent or not * depends on the payload type. */ void cockpit_channel_control (CockpitChannel *self, const gchar *command, JsonObject *options) { JsonObject *object; GBytes *message; const gchar *problem; gchar *problem_copy = NULL; g_return_if_fail (COCKPIT_IS_CHANNEL (self)); g_return_if_fail (command != NULL); if (g_str_equal (command, "done")) { g_return_if_fail (self->priv->sent_done == FALSE); self->priv->sent_done = TRUE; } /* If closing save the close options * and let close send the message */ else if (g_str_equal (command, "close")) { if (!self->priv->close_options) { /* Ref for close_options, freed in parent */ self->priv->close_options = json_object_ref (options); } if (!cockpit_json_get_string (options, "problem", NULL, &problem)) problem = NULL; /* Use a problem copy so it out lasts the value in close_options */ problem_copy = g_strdup (problem); cockpit_channel_close (self, problem_copy); goto out; } if (options) object = json_object_ref (options); else object = json_object_new (); json_object_set_string_member (object, "command", command); json_object_set_string_member (object, "channel", self->priv->id); message = cockpit_json_write_bytes (object); json_object_unref (object); cockpit_transport_send (self->priv->transport, NULL, message); g_bytes_unref (message); out: g_free (problem_copy); }
static gboolean superuser_filter (CockpitPortal *self, const gchar *command, const gchar *channel, JsonObject *options, GBytes *payload) { CockpitPortalFlags flags = COCKPIT_PORTAL_NORMAL; gboolean privileged = FALSE; const gchar *superuser; if (g_str_equal (command, "logout")) { g_debug ("got logout at super proxy"); transition_none (self); return FALSE; } if (g_str_equal (command, "open") && channel) { if (!cockpit_json_get_bool (options, "superuser", FALSE, &privileged)) { if (!cockpit_json_get_string (options, "superuser", NULL, &superuser)) { g_warning ("invalid value for \"superuser\" channel open option"); send_close_channel (self, channel, "protocol-error"); return TRUE; } else if (g_strcmp0 (superuser, "try") == 0) { privileged = TRUE; flags = COCKPIT_PORTAL_FALLBACK; } else if (g_strcmp0 (superuser, "require") == 0) { privileged = TRUE; } else if (superuser) { g_warning ("invalid value for \"superuser\" channel open option: %s", superuser); send_close_channel (self, channel, "protocol-error"); return TRUE; } } if (!privileged) return FALSE; g_debug ("superuser channel open: %s", channel); cockpit_portal_add_channel (self, channel, flags); return TRUE; } return FALSE; }
static CockpitChannelResponse * cockpit_channel_response_create (CockpitWebService *service, CockpitWebResponse *response, CockpitTransport *transport, const gchar *logname, GHashTable *headers, JsonObject *open) { CockpitChannelResponse *chesp; const gchar *payload; JsonObject *done; GBytes *bytes; payload = json_object_get_string_member (open, "payload"); chesp = g_new0 (CockpitChannelResponse, 1); chesp->response = g_object_ref (response); chesp->transport = g_object_ref (transport); chesp->headers = g_hash_table_ref (headers); chesp->channel = cockpit_web_service_unique_channel (service); chesp->open = json_object_ref (open); if (!cockpit_json_get_string (open, "path", chesp->channel, &chesp->logname)) chesp->logname = chesp->channel; json_object_set_string_member (open, "command", "open"); json_object_set_string_member (open, "channel", chesp->channel); /* Special handling for http-stream1, splice in headers, handle injection */ if (g_strcmp0 (payload, "http-stream1") == 0) chesp->transport_recv = g_signal_connect (transport, "recv", G_CALLBACK (on_httpstream_recv), chesp); else chesp->transport_recv = g_signal_connect (transport, "recv", G_CALLBACK (on_transport_recv), chesp); /* Special handling for http-stream2, splice in headers, handle injection */ if (g_strcmp0 (payload, "http-stream2") == 0) chesp->transport_control = g_signal_connect (transport, "control", G_CALLBACK (on_httpstream_control), chesp); else chesp->transport_control = g_signal_connect (transport, "control", G_CALLBACK (on_transport_control), chesp); chesp->transport_closed = g_signal_connect (transport, "closed", G_CALLBACK (on_transport_closed), chesp); bytes = cockpit_json_write_bytes (chesp->open); cockpit_transport_send (transport, NULL, bytes); g_bytes_unref (bytes); done = cockpit_transport_build_json ("command", "done", "channel", chesp->channel, NULL); bytes = cockpit_json_write_bytes (done); json_object_unref (done); cockpit_transport_send (transport, NULL, bytes); g_bytes_unref (bytes); return chesp; }
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 GBytes * substitute_json_string (const gchar *variable, gpointer user_data) { const gchar *value; JsonObject *options = user_data; if (options && cockpit_json_get_string (options, variable, "", &value)) return g_bytes_new (value, strlen (value)); else if (options) g_message ("Couldn't get argument for bridge: got invalid value for '%s'", variable); return g_bytes_new ("", 0); }
static CockpitCreds * create_creds_for_authenticated (const char *user, SessionLoginData *sl, JsonObject *results) { const gchar *fullname = NULL; const gchar *password = NULL; const gchar *gssapi_creds = NULL; /* * Dig the password out of the authorization header, rather than having * cockpit-session pass it back and forth possibly leaking it. */ if (g_str_equal (sl->auth_type, "basic")) password = parse_basic_auth_password (sl->authorization, NULL); if (!cockpit_json_get_string (results, "gssapi-creds", NULL, &gssapi_creds)) { g_warning ("received bad gssapi-creds from cockpit-session"); gssapi_creds = NULL; } if (!cockpit_json_get_string (results, "full-name", NULL, &fullname)) { g_warning ("received bad full-name from cockpit-session"); fullname = NULL; } /* TODO: Try to avoid copying password */ return cockpit_creds_new (user, sl->application, COCKPIT_CRED_FULLNAME, fullname, COCKPIT_CRED_PASSWORD, password, COCKPIT_CRED_RHOST, sl->remote_peer, COCKPIT_CRED_GSSAPI, gssapi_creds, NULL); }
static gboolean on_transport_control (CockpitTransport *transport, const char *command, const gchar *channel_id, JsonObject *options, GBytes *payload, gpointer user_data) { CockpitChannel *self = user_data; CockpitChannelClass *klass; const gchar *problem; if (g_strcmp0 (channel_id, self->priv->id) != 0) return FALSE; klass = COCKPIT_CHANNEL_GET_CLASS (self); if (g_str_equal (command, "options")) { if (klass->options) (klass->options) (self, options); return TRUE; } else if (g_str_equal (command, "done")) { if (self->priv->received_done) { g_warning ("%s: channel received second done", self->priv->id); cockpit_channel_close (self, "protocol-error"); } else { self->priv->received_done = TRUE; if (self->priv->ready) { if (klass->done) (klass->done) (self); } } return TRUE; } else if (g_str_equal (command, "close")) { g_debug ("close channel %s", channel_id); if (!cockpit_json_get_string (options, "problem", NULL, &problem)) problem = NULL; cockpit_channel_close (self, problem); } return FALSE; }
/** * cockpit_channel_open: * @transport: the transport to send/receive messages on * @number: the channel number * @options: the options to open the channel. * * Open a channel for the 'payload' field in @options. Other fields * in @options are dependent on the channel type. * * Guarantee: channel will not close immediately, even on invalid input. * * Returns: (transfer full): the new channel */ CockpitChannel * cockpit_channel_open (CockpitTransport *transport, const gchar *id, JsonObject *options) { CockpitChannel *channel; GType channel_type; const gchar *payload; if (!cockpit_json_get_string (options, "payload", NULL, &payload)) payload = NULL; /* TODO: We need to migrate away from dbus-json1 */ if (g_strcmp0 (payload, "dbus-json1") == 0) channel_type = COCKPIT_TYPE_DBUS_JSON1; else if (g_strcmp0 (payload, "dbus-json2") == 0) channel_type = COCKPIT_TYPE_DBUS_JSON; else if (g_strcmp0 (payload, "rest-json1") == 0) channel_type = COCKPIT_TYPE_REST_JSON; else if (g_strcmp0 (payload, "text-stream") == 0) channel_type = COCKPIT_TYPE_TEXT_STREAM; else if (g_strcmp0 (payload, "resource1") == 0) channel_type = COCKPIT_TYPE_RESOURCE; else if (g_strcmp0 (payload, "null") == 0) channel_type = COCKPIT_TYPE_NULL_CHANNEL; else channel_type = COCKPIT_TYPE_CHANNEL; channel = g_object_new (channel_type, "transport", transport, "id", id, "options", options, NULL); if (channel_type == COCKPIT_TYPE_CHANNEL) { if (payload) { g_warning ("agent doesn't support payloads of type: %s", payload); cockpit_channel_close (channel, "not-supported"); } else { g_warning ("no payload type present in request to open channel"); cockpit_channel_close (channel, "protocol-error"); } } return channel; }
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 void cockpit_web_socket_stream_prepare (CockpitChannel *channel) { CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (channel); CockpitConnectable *connectable = NULL; JsonObject *options; const gchar *path; gboolean started = FALSE; COCKPIT_CHANNEL_CLASS (cockpit_web_socket_stream_parent_class)->prepare (channel); if (self->closed) goto out; connectable = cockpit_channel_parse_stream (channel); if (!connectable) goto out; options = cockpit_channel_get_options (channel); if (!cockpit_json_get_string (options, "path", NULL, &path)) { g_warning ("%s: bad \"path\" field in WebSocket stream request", self->origin); goto out; } else if (path == NULL || path[0] != '/') { g_warning ("%s: invalid or missing \"path\" field in WebSocket stream request", self->origin); goto out; } self->url = g_strdup_printf ("%s://%s%s", connectable->tls ? "wss" : "ws", connectable->name, path); self->origin = g_strdup_printf ("%s://%s", connectable->tls ? "https" : "http", connectable->name); /* Parsed elsewhere */ self->binary = json_object_has_member (options, "binary"); cockpit_connect_stream_full (connectable, NULL, on_socket_connect, g_object_ref (self)); started = TRUE; out: if (connectable) { cockpit_connectable_unref (connectable); if (!started) cockpit_channel_close (channel, "protocol-error"); } }