Пример #1
0
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);
}
Пример #2
0
static void
cockpit_pipe_transport_close (CockpitTransport *transport,
                              const gchar *problem)
{
  CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (transport);
  cockpit_pipe_close (self->pipe, problem);
}
Пример #3
0
static void
cockpit_interact_transport_close (CockpitTransport *transport,
                                  const gchar *problem)
{
  CockpitInteractTransport *self = COCKPIT_INTERACT_TRANSPORT (transport);
  cockpit_pipe_close (self->pipe, problem);
}
Пример #4
0
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);
    }
}
Пример #5
0
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);
    }
}
Пример #6
0
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);
}
Пример #7
0
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);
    }
}
Пример #8
0
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;
}
Пример #9
0
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");
        }
    }
}
Пример #10
0
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);
}
Пример #11
0
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);
}
Пример #12
0
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;
}
Пример #13
0
/**
 * 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);
}
Пример #14
0
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);
}
Пример #15
0
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);
}