static void cockpit_ssh_transport_attach_pipe (CockpitSshTransport *self) { g_return_if_fail (self->pipe == NULL); g_return_if_fail (self->auth_process != NULL); if (self->agent) cockpit_ssh_agent_close (self->agent); self->pipe = cockpit_auth_process_claim_as_pipe (self->auth_process); self->read_sig = g_signal_connect (self->pipe, "read", G_CALLBACK (on_pipe_read), self); self->close_sig = g_signal_connect (self->pipe, "close", G_CALLBACK (on_pipe_close), self); cockpit_ssh_transport_remove_auth_process (self); while (g_queue_peek_head (self->queue)) { GBytes *block = g_queue_pop_head (self->queue); cockpit_pipe_write (self->pipe, block); g_bytes_unref (block); } if (self->closing && !self->closed) cockpit_pipe_close (self->pipe, NULL); }
static void cockpit_pipe_transport_close (CockpitTransport *transport, const gchar *problem) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (transport); cockpit_pipe_close (self->pipe, problem); }
static void cockpit_interact_transport_close (CockpitTransport *transport, const gchar *problem) { CockpitInteractTransport *self = COCKPIT_INTERACT_TRANSPORT (transport); cockpit_pipe_close (self->pipe, problem); }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *data, gboolean end_of_data, gpointer user_data) { CockpitTextStream *self = user_data; CockpitChannel *channel = user_data; GBytes *message; GBytes *clean; if (data->len || !end_of_data) { /* When array is reffed, this just clears byte array */ g_byte_array_ref (data); message = g_byte_array_free_to_bytes (data); clean = check_utf8_and_force_if_necessary (message); cockpit_channel_send (channel, clean); g_bytes_unref (message); g_bytes_unref (clean); } /* Close the pipe when writing is done */ if (end_of_data && self->open) { g_debug ("%s: end of data, closing pipe", self->name); cockpit_pipe_close (pipe, NULL); } }
static void cockpit_ssh_transport_close (CockpitTransport *transport, const gchar *problem) { CockpitSshTransport *self = COCKPIT_SSH_TRANSPORT (transport); if (self->closed) return; self->closing = TRUE; /* If still connecting and there isn't a problem * don't do anything yet */ if (self->connecting && !problem) return; if (self->pipe) { cockpit_pipe_close (self->pipe, problem); } else if (self->auth_process) { cockpit_ssh_transport_remove_auth_process (self); self->closed = TRUE; cockpit_transport_emit_closed (COCKPIT_TRANSPORT (self), problem); } }
static void on_newusers_close (CockpitPipe *pipe, const gchar *problem, gpointer user_data) { CommitAdmin1 *context = user_data; CockpitPipe *next; const gchar *argv[] = { cockpit_bridge_path_chpasswd, "--encrypted", NULL }; if (!check_pipe_exit_status (pipe, problem, "couldn't run newusers command")) { g_dbus_method_invocation_return_error (context->invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, _("Couldn't create new users")); commit_passwd1_free (context); } else { g_debug ("batch changing user passwords"); next = cockpit_pipe_spawn (argv, NULL, NULL, COCKPIT_PIPE_FLAGS_NONE); g_signal_connect (next, "close", G_CALLBACK (on_chpasswd_close), context); cockpit_pipe_write (next, context->chpasswd); cockpit_pipe_close (next, NULL); } g_object_unref (pipe); }
static void perform_usermod (CommitAdmin1 *context) { CockpitPipe *pipe; GHashTableIter iter; gpointer name; gpointer grouplist; const gchar *argv[] = { cockpit_bridge_path_usermod, "xx", "--append", "--group", "yy", NULL }; g_hash_table_iter_init (&iter, context->usermod); if (g_hash_table_iter_next (&iter, &name, &grouplist)) { argv[1] = (gchar *)name; argv[4] = ((GString *)grouplist)->str; g_debug ("adding user '%s' to groups: %s", argv[1], argv[4]); pipe = cockpit_pipe_spawn (argv, NULL, NULL, COCKPIT_PIPE_FLAGS_NONE); g_hash_table_iter_remove (&iter); g_signal_connect (pipe, "close", G_CALLBACK (on_usermod_close), context); cockpit_pipe_close (pipe, NULL); } else { /* All done, success */ g_dbus_method_invocation_return_value (context->invocation, NULL); commit_passwd1_free (context); } }
static CockpitPipe * spawn_session_process (const gchar *type, GBytes *input, const gchar *remote_peer, CockpitPipe **auth_pipe) { CockpitPipe *pipe; int pwfds[2] = { -1, -1 }; GError *error = NULL; GPid pid = 0; gint in_fd = -1; gint out_fd = -1; const gchar *argv[] = { cockpit_ws_session_program, type, remote_peer ? remote_peer : "", NULL, }; g_return_val_if_fail (input != NULL, NULL); if (socketpair (PF_UNIX, SOCK_STREAM, 0, pwfds) < 0) g_return_val_if_reached (NULL); g_debug ("spawning %s", cockpit_ws_session_program); if (!g_spawn_async_with_pipes (NULL, (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, session_child_setup, GINT_TO_POINTER (pwfds[1]), &pid, &in_fd, &out_fd, NULL, &error)) { g_warning ("failed to start %s: %s", cockpit_ws_session_program, error->message); g_error_free (error); return NULL; } else { pipe = g_object_new (COCKPIT_TYPE_PIPE, "name", "localhost", "pid", pid, "in-fd", out_fd, "out-fd", in_fd, NULL); /* Child process end of pipe */ close (pwfds[1]); *auth_pipe = cockpit_pipe_new ("auth-pipe", pwfds[0], pwfds[0]); cockpit_pipe_write (*auth_pipe, input); cockpit_pipe_close (*auth_pipe, NULL); } return pipe; }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); GBytes *message; GBytes *payload; gchar *channel; guint32 size; for (;;) { if (input->len < sizeof (size)) { if (!end_of_data) g_debug ("%s: want more data", self->name); break; } memcpy (&size, input->data, sizeof (size)); size = GUINT32_FROM_BE (size); if (input->len < size + sizeof (size)) { g_debug ("%s: want more data", self->name); break; } message = cockpit_pipe_consume (input, sizeof (size), size); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->name, (int)size); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_warning ("%s: received truncated %d byte frame", self->name, input->len); cockpit_pipe_close (pipe, "internal-error"); } } }
static void cockpit_text_stream_close (CockpitChannel *channel, const gchar *problem) { CockpitTextStream *self = COCKPIT_TEXT_STREAM (channel); self->closing = TRUE; /* * If closed, call base class handler directly. Otherwise ask * our pipe to close first, which will come back here. */ if (self->open) cockpit_pipe_close (self->pipe, problem); else COCKPIT_CHANNEL_CLASS (cockpit_text_stream_parent_class)->close (channel, problem); }
static void cockpit_text_stream_dispose (GObject *object) { CockpitTextStream *self = COCKPIT_TEXT_STREAM (object); if (self->pipe) { if (self->open) cockpit_pipe_close (self->pipe, "terminated"); if (self->sig_read) g_signal_handler_disconnect (self->pipe, self->sig_read); if (self->sig_close) g_signal_handler_disconnect (self->pipe, self->sig_close); self->sig_read = self->sig_close = 0; } G_OBJECT_CLASS (cockpit_text_stream_parent_class)->dispose (object); }
static CockpitPipe * spawn_session_process (const gchar *user, GBytes *password, const gchar *remote_peer, CockpitPipe **auth_pipe) { CockpitPipe *pipe; int pwfds[2] = { -1, -1 }; GError *error = NULL; const gchar **argv; char autharg[32]; GPid pid = 0; gint in_fd = -1; gint out_fd = -1; const gchar *argv_password[] = { cockpit_ws_session_program, "-p", autharg, user ? user : "", remote_peer ? remote_peer : "", cockpit_ws_agent_program, NULL, }; const gchar *argv_noauth[] = { cockpit_ws_session_program, user ? user : "", remote_peer ? remote_peer : "", cockpit_ws_agent_program, NULL, }; if (password) { if (socketpair (PF_UNIX, SOCK_STREAM, 0, pwfds) < 0) g_return_val_if_reached (NULL); g_snprintf (autharg, sizeof (autharg), "%d", pwfds[1]); argv = argv_password; } else { argv = argv_noauth; } if (!g_spawn_async_with_pipes (NULL, (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, NULL, NULL, &pid, &in_fd, &out_fd, NULL, &error)) { g_warning ("failed to start %s: %s", cockpit_ws_session_program, error->message); g_error_free (error); return NULL; } else { pipe = g_object_new (COCKPIT_TYPE_PIPE, "pid", pid, "in-fd", out_fd, "out-fd", in_fd, NULL); if (password) { /* Child process end of pipe */ close (pwfds[1]); *auth_pipe = cockpit_pipe_new ("password-pipe", pwfds[0], pwfds[0]); cockpit_pipe_write (*auth_pipe, password); cockpit_pipe_close (*auth_pipe, NULL); } } return pipe; }
/** * cockpit_transport_read_from_pipe: * * Meant to be used in a "read" handler for a #CockpitPipe * Closed is pointer to a boolean value that may be updated * during the read and parse loop. */ void cockpit_transport_read_from_pipe (CockpitTransport *self, const gchar *logname, CockpitPipe *pipe, gboolean *closed, GByteArray *input, gboolean end_of_data) { GBytes *message; GBytes *payload; gchar *channel; guint32 i, size; gchar *data; /* This may be updated during the loop. */ g_assert (closed != NULL); g_object_ref (self); while (!*closed) { size = 0; data = (gchar *)input->data; for (i = 0; i < input->len; i++) { /* Check invalid characters, prevent integer overflow, limit max length */ if (i > 7 || data[i] < '0' || data[i] > '9') break; size *= 10; size += data[i] - '0'; } if (i == input->len) { if (!end_of_data) g_debug ("%s: want more data", logname); break; } if (data[i] != '\n') { g_warning ("%s: incorrect protocol: received invalid length prefix", logname); cockpit_pipe_close (pipe, "protocol-error"); break; } if (input->len < i + 1 + size) { g_debug ("%s: want more data 2", logname); break; } message = cockpit_pipe_consume (input, i + 1, size, 0); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", logname, (int)size); cockpit_transport_emit_recv (self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_debug ("%s: received truncated %d byte frame", logname, input->len); cockpit_pipe_close (pipe, "disconnected"); } } g_object_unref (self); }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); GBytes *message; GBytes *payload; gchar *channel; guint32 i, size; gchar *data; g_object_ref (self); for (;;) { size = 0; data = (gchar *)input->data; for (i = 0; i < input->len; i++) { /* Check invalid characters, prevent integer overflow, limit max length */ if (i > 7 || data[i] < '0' || data[i] > '9') break; size *= 10; size += data[i] - '0'; } if (i == input->len) { if (!end_of_data) g_debug ("%s: want more data", self->name); break; } if (data[i] != '\n') { g_warning ("%s: incorrect protocol: received invalid length prefix", self->name); cockpit_pipe_close (pipe, "protocol-error"); break; } if (input->len < i + 1 + size) { g_debug ("%s: want more data 2", self->name); break; } message = cockpit_pipe_consume (input, i + 1, size, 0); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->name, (int)size); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_debug ("%s: received truncated %d byte frame", self->name, input->len); cockpit_pipe_close (pipe, "disconnected"); } } g_object_unref (self); }
static void setup_commit_passwd1 (GVariant *parameters, GDBusMethodInvocation *invocation) { const gchar *mechanism; GVariant *transferred = NULL; const gchar **lines; GHashTable *users = NULL; GHashTable *groups = NULL; GString *chpasswd = NULL; GString *newusers = NULL; CommitAdmin1 *context; CockpitPipe *pipe; gsize length, i, j; gchar **parts; GBytes *bytes; GVariant *pwdata = NULL; GVariant *grdata = NULL; GHashTable *usermod; gchar **memlist; GString *string; gboolean user_exists; /* We are getting crypted passwords so we need to use * --crypt-method=NONE with newusers and chpasswd so that the string * is installed unchanged. Unfortunately, newusers might or might * not support the --crypt-method option, depending on whether it * was compiled with or without PAM. When the option is missing, we * fix up the password afterwards via chpasswd. * * However, newusers needs some valid password to create new users. * Thus, we need a good random string that passes all password * quality criteria, and we just use the crpyted password for that. */ const gchar *argv[] = { cockpit_bridge_path_newusers, "--crypt-method=NONE", NULL }; if (!cockpit_bridge_have_newusers_crypt_method) argv[1] = NULL; g_variant_get (parameters, "(&sv)", &mechanism, &transferred); if (!g_str_equal (mechanism, "passwd1")) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Unsupported setup mechanism: %s", mechanism); goto out; } if (!g_variant_is_of_type (transferred, G_VARIANT_TYPE ("(asas)"))) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Bad data passed for passwd1 mechanism"); goto out; } users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); if (!fgetpwent_callback (add_name_to_hashtable, users) || !fgetgrent_callback (add_group_to_hashtable, groups)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, _("Couldn't list local users")); goto out; } g_debug ("starting setup synchronization"); g_variant_get (transferred, "(@as@as)", &pwdata, &grdata); chpasswd = g_string_new (""); newusers = g_string_new (""); usermod = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, string_free); lines = g_variant_get_strv (pwdata, &length); for (i = 0; i < length; i++) { parts = g_strsplit(lines[i], ":", 3); user_exists = (g_hash_table_lookup (users, parts[0]) != NULL); if (!user_exists) { g_string_append (newusers, lines[i]); g_string_append_c (newusers, '\n'); } if (user_exists || !cockpit_bridge_have_newusers_crypt_method) { g_string_append_printf (chpasswd, "%s:%s\n", parts[0], parts[1]); } g_strfreev (parts); } g_free (lines); lines = g_variant_get_strv (grdata, &length); for (i = 0; i < length; i++) { parts = g_strsplit(lines[i], ":", 4); if (g_hash_table_lookup (groups, parts[0])) { memlist = g_strsplit (parts[3], ",", -1); for (j = 0; memlist[j] != NULL; j++) { string = g_hash_table_lookup (usermod, memlist[j]); if (!string) { string = g_string_new (""); g_hash_table_insert (usermod, g_strdup (memlist[j]), string); } if (string->len > 0) g_string_append_c (string, ','); g_string_append (string, parts[0]); } g_strfreev (memlist); } g_strfreev (parts); } g_free (lines); context = g_new0 (CommitAdmin1, 1); context->invocation = g_object_ref (invocation); context->chpasswd = g_string_free_to_bytes (chpasswd); context->usermod = usermod; g_debug ("batch creating new users"); bytes = g_string_free_to_bytes (newusers); pipe = cockpit_pipe_spawn (argv, NULL, NULL, COCKPIT_PIPE_FLAGS_NONE); g_signal_connect (pipe, "close", G_CALLBACK (on_newusers_close), context); cockpit_pipe_write (pipe, bytes); cockpit_pipe_close (pipe, NULL); g_bytes_unref (bytes); out: if (users) g_hash_table_unref (users); if (groups) g_hash_table_unref (groups); if (pwdata) g_variant_unref (pwdata); if (grdata) g_variant_unref (grdata); g_variant_unref (transferred); }