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 const gchar * process_transport_init (CockpitWebService *self, CockpitTransport *transport, JsonObject *options) { JsonObject *object; GBytes *payload; gint64 version; if (!cockpit_json_get_int (options, "version", -1, &version)) { g_warning ("invalid version field in init message"); return "protocol-error"; } if (version == 1) { g_debug ("received init message"); self->init_received = TRUE; g_object_set_data_full (G_OBJECT (transport), "init", json_object_ref (options), (GDestroyNotify) json_object_unref); /* Always send an init message down the new transport */ object = cockpit_transport_build_json ("command", "init", NULL); json_object_set_int_member (object, "version", 1); json_object_set_string_member (object, "host", "localhost"); payload = cockpit_json_write_bytes (object); json_object_unref (object); cockpit_transport_send (transport, NULL, payload); g_bytes_unref (payload); } else { g_message ("unsupported version of cockpit protocol: %" G_GINT64_FORMAT, version); return "not-supported"; } g_signal_emit (self, sig_transport_init, 0); return NULL; }
void cockpit_channel_response_serve (CockpitWebService *service, GHashTable *in_headers, CockpitWebResponse *response, const gchar *where, const gchar *path) { CockpitChannelResponse *chesp = NULL; CockpitTransport *transport = NULL; CockpitCacheType cache_type = COCKPIT_WEB_RESPONSE_CACHE_PRIVATE; const gchar *host = NULL; const gchar *pragma; gchar *quoted_etag = NULL; GHashTable *out_headers = NULL; gchar *val = NULL; gboolean handled = FALSE; GHashTableIter iter; const gchar *checksum; JsonObject *object = NULL; JsonObject *heads; gchar *channel = NULL; gchar *language = NULL; gpointer key; gpointer value; g_return_if_fail (COCKPIT_IS_WEB_SERVICE (service)); g_return_if_fail (in_headers != NULL); g_return_if_fail (COCKPIT_IS_WEB_RESPONSE (response)); g_return_if_fail (path != NULL); if (where == NULL) { host = "localhost"; } else if (where[0] == '@') { host = where + 1; } else if (where[0] == '$') { quoted_etag = g_strdup_printf ("\"%s\"", where); cache_type = COCKPIT_WEB_RESPONSE_CACHE_FOREVER; pragma = g_hash_table_lookup (in_headers, "Pragma"); if ((!pragma || !strstr (pragma, "no-cache")) && (g_strcmp0 (g_hash_table_lookup (in_headers, "If-None-Match"), where) == 0 || g_strcmp0 (g_hash_table_lookup (in_headers, "If-None-Match"), quoted_etag) == 0)) { cockpit_web_response_headers (response, 304, "Not Modified", 0, "ETag", quoted_etag, NULL); cockpit_web_response_complete (response); handled = TRUE; goto out; } transport = cockpit_web_service_find_transport (service, where + 1); if (!transport) goto out; host = cockpit_web_service_get_host (service, transport); if (!host) { g_warn_if_reached (); goto out; } } else { goto out; } cockpit_web_response_set_cache_type (response, cache_type); object = cockpit_transport_build_json ("command", "open", "payload", "http-stream1", "internal", "packages", "method", "GET", "host", host, "path", path, "binary", "raw", NULL); if (!transport) { transport = cockpit_web_service_ensure_transport (service, object); if (!transport) goto out; } if (where) { /* * Maybe send back a redirect to the checksum url. We only do this if actually * accessing a file, and not a some sort of data like '/checksum', or a root path * like '/' */ if (where[0] == '@' && strchr (path, '.')) { checksum = cockpit_web_service_get_checksum (service, transport); if (checksum) { handled = redirect_to_checksum_path (service, response, checksum, path); goto out; } } } out_headers = cockpit_web_server_new_table (); channel = cockpit_web_service_unique_channel (service); json_object_set_string_member (object, "channel", channel); if (quoted_etag) { /* * If we have a checksum, then use it as an ETag. It is intentional that * a cockpit-bridge version could (in the future) override this. */ g_hash_table_insert (out_headers, g_strdup ("ETag"), quoted_etag); quoted_etag = NULL; } heads = json_object_new (); g_hash_table_iter_init (&iter, in_headers); while (g_hash_table_iter_next (&iter, &key, &value)) { val = NULL; if (g_ascii_strcasecmp (key, "Host") == 0 || g_ascii_strcasecmp (key, "Cookie") == 0 || g_ascii_strcasecmp (key, "Referer") == 0 || g_ascii_strcasecmp (key, "Connection") == 0 || g_ascii_strcasecmp (key, "Pragma") == 0 || g_ascii_strcasecmp (key, "Cache-Control") == 0 || g_ascii_strcasecmp (key, "User-Agent") == 0 || g_ascii_strcasecmp (key, "Accept-Charset") == 0 || g_ascii_strcasecmp (key, "Accept-Ranges") == 0 || g_ascii_strcasecmp (key, "Content-Length") == 0 || g_ascii_strcasecmp (key, "Content-MD5") == 0 || g_ascii_strcasecmp (key, "Content-Range") == 0 || g_ascii_strcasecmp (key, "Range") == 0 || g_ascii_strcasecmp (key, "TE") == 0 || g_ascii_strcasecmp (key, "Trailer") == 0 || g_ascii_strcasecmp (key, "Upgrade") == 0 || g_ascii_strcasecmp (key, "Transfer-Encoding") == 0) continue; json_object_set_string_member (heads, key, value); g_free (val); } /* Parse the language out of the CockpitLang cookie */ language = cockpit_web_server_parse_cookie (in_headers, "CockpitLang"); if (language) json_object_set_string_member (heads, "Accept-Language", language); json_object_set_string_member (heads, "Host", host); json_object_set_object_member (object, "headers", heads); chesp = cockpit_channel_response_create (service, response, transport, cockpit_web_response_get_path (response), out_headers, object); if (!where) chesp->inject = cockpit_channel_inject_new (service, path); handled = TRUE; out: g_free (language); if (object) json_object_unref (object); g_free (quoted_etag); if (out_headers) g_hash_table_unref (out_headers); g_free (channel); if (!handled) cockpit_web_response_error (response, 404, NULL, NULL); }
int main (int argc, char *argv[]) { GOptionContext *context; JsonObject *request = NULL; JsonObject *reply = NULL; GError *error = NULL; const gchar *env; const gchar *command = NULL; const gchar *field = NULL; const gchar *response = NULL; gchar *user = NULL; gchar *cookie = NULL; gchar *challenge = NULL; gint ret = 1; static GOptionEntry entries[] = { { NULL } }; /* Debugging issues during testing */ #if WITH_DEBUG signal (SIGABRT, cockpit_test_signal_backtrace); signal (SIGSEGV, cockpit_test_signal_backtrace); #endif context = g_option_context_new (NULL); g_option_context_add_main_entries (context, entries, NULL); g_option_context_set_description (context, "cockpit-bridge uses cockpit-askpass during password prompts.\n"); g_option_context_parse (context, &argc, &argv, &error); g_option_context_free (context); if (error) { g_printerr ("cockpit-askpass: %s\n", error->message); g_error_free (error); return 1; } if (isatty (0)) { g_printerr ("cockpit-askpass: this command is not meant to be run directly\n"); return 2; } /* * We don't send an init message. This is meant to be used either after an * "init" message has been sent, or with a caller that makes an exception for * the "authorize" command message. */ env = g_getenv ("USER"); user = cockpit_hex_encode (env ? env : "", -1); challenge = g_strdup_printf ("plain1:%s:", user); cookie = g_strdup_printf ("askpass%u%u", (unsigned int)getpid (), (unsigned int)time (NULL)); request = cockpit_transport_build_json ("command", "authorize", "challenge", challenge, "cookie", cookie, NULL); /* Yes, we write to stdin which we expect to be a socketpair() */ if (write_control_message (STDIN_FILENO, request)) { reply = read_control_message (STDIN_FILENO); if (reply) { if (cockpit_json_get_string (reply, "command", "", &command) && cockpit_json_get_string (reply, "cookie", "", &field) && cockpit_json_get_string (reply, "response", "", &response)) { if (g_str_equal (field, cookie) && g_str_equal (command, "authorize")) { /* The password is written back on stdout */ if (write_all (STDOUT_FILENO, response, -1) && write_all (STDOUT_FILENO, "\n", 1)) ret = 0; } else { g_message ("askpass received unexpected %s control message", command); } } else { g_message ("askpass response has invalid control message authorize fields"); } } } g_free (cookie); g_free (challenge); g_free (user); /* Clear the password memory owned by JsonObject */ if (response) cockpit_memory_clear ((gchar *)response, -1); if (request) json_object_unref (request); if (reply) json_object_unref (reply); return ret; }