static gboolean on_handle_stream_socket (CockpitWebServer *server, const gchar *path, GIOStream *io_stream, GHashTable *headers, GByteArray *input, guint in_length, gpointer user_data) { CockpitTransport *transport; const gchar *query = NULL; CockpitCreds *creds; CockpitPipe *pipe; gchar *value; gchar **env; if (!g_str_has_prefix (path, "/cockpit/socket")) return FALSE; if (path[15] == '?') query = path + 16; else if (path[15] != '\0') return FALSE; if (service) { g_object_ref (service); } else { value = g_strdup_printf ("%d", server_port); env = g_environ_setenv (g_get_environ (), "COCKPIT_TEST_SERVER_PORT", value, TRUE); creds = cockpit_creds_new (g_get_user_name (), "test", COCKPIT_CRED_CSRF_TOKEN, "myspecialtoken", NULL); pipe = cockpit_pipe_spawn ((const gchar **)bridge_argv, (const gchar **)env, NULL, FALSE); transport = cockpit_pipe_transport_new (pipe); service = cockpit_web_service_new (creds, transport); cockpit_creds_unref (creds); g_object_unref (transport); g_object_unref (pipe); g_free (value); g_strfreev (env); /* Clear the pointer automatically when service is done */ g_object_add_weak_pointer (G_OBJECT (service), (gpointer *)&service); } if (query) cockpit_channel_socket_open (service, "/cockpit/socket", query, io_stream, headers, input); else cockpit_web_service_socket (service, "/cockpit/socket", io_stream, headers, input); /* Keeps ref on itself until it closes */ g_object_unref (service); return TRUE; }
/* Called by @server when handling HTTP requests to /socket - runs in a separate * thread dedicated to the request so it may do blocking I/O */ static gboolean on_handle_stream_socket (CockpitWebServer *server, CockpitWebServerRequestType reqtype, const gchar *resource, GIOStream *io_stream, GHashTable *headers, GByteArray *input, guint in_length, gpointer user_data) { CockpitWebService *service; CockpitCreds *creds; if (!g_str_equal (resource, "/socket")) return FALSE; creds = cockpit_creds_new (g_get_user_name (), NULL); service = cockpit_web_service_new (creds, NULL); cockpit_web_service_socket (service, io_stream, headers, input); /* Keeps ref on itself until it closes */ g_object_unref (service); cockpit_creds_unref (creds); return TRUE; }
static void cockpit_auth_remote_login_async (CockpitAuth *self, GHashTable *headers, const gchar *remote_peer, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *task; CockpitCreds *creds = NULL; RemoteLoginData *rl; const gchar *password; GBytes *input; gchar *type = NULL; gchar *user = NULL; task = g_simple_async_result_new (G_OBJECT (self), callback, user_data, cockpit_auth_remote_login_async); input = cockpit_auth_parse_authorization (headers, &type); if (type && input && g_str_equal (type, "basic")) { password = parse_basic_auth_password (input, &user); if (password && user) { creds = cockpit_creds_new (user, COCKPIT_CRED_PASSWORD, password, COCKPIT_CRED_RHOST, remote_peer, NULL); } g_free (user); } if (creds) { rl = g_new0 (RemoteLoginData, 1); rl->creds = creds; rl->transport = g_object_new (COCKPIT_TYPE_SSH_TRANSPORT, "host", "127.0.0.1", "port", cockpit_ws_specific_ssh_port, "command", cockpit_ws_bridge_program, "creds", creds, "ignore-key", TRUE, NULL); g_simple_async_result_set_op_res_gpointer (task, rl, remote_login_data_free); g_signal_connect (rl->transport, "result", G_CALLBACK (on_remote_login_done), g_object_ref (task)); } else { g_simple_async_result_set_error (task, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED, "Basic authentication required"); g_simple_async_result_complete_in_idle (task); } g_free (type); g_object_unref (task); }
static CockpitCreds * create_creds_for_spawn_authenticated (CockpitAuth *self, const gchar *user, AuthData *ad, JsonObject *results, const gchar *raw_data) { const gchar *password = NULL; const gchar *gssapi_creds = NULL; CockpitCreds *creds = NULL; gchar *csrf_token; /* * Dig the password out of the authorization header, rather than having * passing it back and forth possibly leaking it. */ if (g_str_equal (ad->auth_type, "basic") || g_str_equal (ad->auth_type, SSH_SECTION)) { password = parse_basic_auth_password (ad->authorization, NULL); } if (!cockpit_json_get_string (results, "gssapi-creds", NULL, &gssapi_creds)) { g_warning ("received bad gssapi-creds"); gssapi_creds = NULL; } csrf_token = cockpit_auth_nonce (self); creds = cockpit_creds_new (user, ad->application, COCKPIT_CRED_LOGIN_DATA, raw_data, COCKPIT_CRED_PASSWORD, password, COCKPIT_CRED_RHOST, ad->remote_peer, COCKPIT_CRED_GSSAPI, gssapi_creds, COCKPIT_CRED_CSRF_TOKEN, csrf_token, NULL); g_free (csrf_token); return creds; }
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 CockpitCreds * parse_auth_results (LoginData *login, GError **error) { CockpitCreds *creds = NULL; GByteArray *buffer; GError *json_error = NULL; const gchar *pam_user; JsonObject *results; gconstpointer data; gint64 code = -1; gchar *password; gsize length; buffer = cockpit_pipe_get_buffer (login->auth_pipe); results = cockpit_json_parse_object ((const gchar *)buffer->data, buffer->len, &json_error); if (g_error_matches (json_error, JSON_PARSER_ERROR, JSON_PARSER_ERROR_INVALID_DATA)) { g_message ("got non-utf8 user name from cockpit-session"); g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Login user name is not UTF8 encoded"); g_error_free (json_error); return NULL; } if (!results) { g_warning ("couldn't parse session auth output: %s", json_error->message); g_error_free (json_error); return NULL; } if (!cockpit_json_get_int (results, "pam-result", -1, &code) || code < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid data from session process: bad PAM result"); } else if (code == PAM_SUCCESS) { if (!cockpit_json_get_string (results, "user", NULL, &pam_user) || !pam_user) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid data from session process: missing user"); } else { g_debug ("user authenticated as %s", pam_user); /* TODO: Try to avoid copying password */ data = g_bytes_get_data (login->password, &length); password = g_strndup (data, length); creds = cockpit_creds_new (pam_user, COCKPIT_CRED_PASSWORD, password, COCKPIT_CRED_RHOST, login->remote_peer, NULL); g_free (password); } } else if (code == PAM_AUTH_ERR || code == PAM_USER_UNKNOWN) { g_debug ("authentication failed: %d", (int)code); g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED, "Authentication failed"); } else { g_debug ("pam error: %d", (int)code); g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "%s", pam_strerror (NULL, code)); } json_object_unref (results); return creds; }
static gboolean on_handle_stream_socket (CockpitWebServer *server, const gchar *path, GIOStream *io_stream, GHashTable *headers, GByteArray *input, gpointer user_data) { CockpitTransport *transport; const gchar *query = NULL; CockpitCreds *creds; int session_stdin = -1; int session_stdout = -1; GError *error = NULL; GPid pid = 0; gchar *value; gchar **env; gchar **argv; if (!g_str_has_prefix (path, "/cockpit/socket")) return FALSE; if (path[15] == '?') { query = path + 16; } else if (path[15] != '\0') { return FALSE; } if (service) { g_object_ref (service); } else { g_clear_object (&bridge); value = g_strdup_printf ("%d", server_port); env = g_environ_setenv (g_get_environ (), "COCKPIT_TEST_SERVER_PORT", value, TRUE); argv = g_strdupv (bridge_argv); if (query) argv[g_strv_length (argv) - 1] = g_strdup (query); g_spawn_async_with_pipes (NULL, argv, env, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &session_stdin, &session_stdout, NULL, &error); g_strfreev (env); g_free (argv); g_free (value); if (error) { g_critical ("couldn't run bridge %s: %s", bridge_argv[0], error->message); return FALSE; } bridge = g_object_new (COCKPIT_TYPE_PIPE, "name", "test-server-bridge", "in-fd", session_stdout, "out-fd", session_stdin, "pid", pid, NULL); creds = cockpit_creds_new (g_get_user_name (), "test", COCKPIT_CRED_CSRF_TOKEN, "myspecialtoken", NULL); transport = cockpit_pipe_transport_new (bridge); service = cockpit_web_service_new (creds, transport); cockpit_creds_unref (creds); g_object_unref (transport); /* Clear the pointer automatically when service is done */ g_object_add_weak_pointer (G_OBJECT (service), (gpointer *)&service); } cockpit_web_service_socket (service, path, io_stream, headers, input); /* Keeps ref on itself until it closes */ g_object_unref (service); return TRUE; }