static gboolean
process_logout (CockpitWebService *self,
                JsonObject *options)
{
  gboolean disconnect;

  if (!cockpit_json_get_bool (options, "disconnect", FALSE, &disconnect))
    {
      g_warning ("received 'logout' command with invalid 'disconnect' field");
      return FALSE;
    }

  /* Makes the credentials unusable */
  cockpit_creds_poison (self->creds);

  /* Destroys our web service, disconnects everything */
  if (disconnect)
    {
      g_info ("Logging out session from %s", cockpit_creds_get_rhost (self->creds));
      g_object_run_dispose (G_OBJECT (self));
    }
  else
    {
      g_info ("Deauthorizing session from %s", cockpit_creds_get_rhost (self->creds));
    }

  send_socket_hints (self, "credential", "none");
  return TRUE;
}
Beispiel #2
0
/*
 * 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");
}
Beispiel #3
0
/**
 * cockpit_channel_get_bool_option:
 * @self: a channel
 * @name: the option name
 *
 * Called by implementations to get an int value from the
 * channel's options.
 *
 * Returns: TRUE of option set, FALSE if missing or set to false
 */
gboolean
cockpit_channel_get_bool_option (CockpitChannel *self,
                                 const gchar *name)
{
  gboolean value;
  if (!cockpit_json_get_bool (self->priv->open_options, name, FALSE, &value))
    value = FALSE;
  return value;
}
Beispiel #4
0
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;
}
Beispiel #5
0
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);
        }
    }

  /*
   * The default here, can change from FALSE to TRUE over time once we assume that all
   * cockpit-ws participants have been upgraded sufficiently. The default when we're
   * on the channel creation side is to handle flow control.
   */
  if (!cockpit_json_get_bool (options, "flow-control", FALSE, &self->priv->flow_control))
    {
      cockpit_channel_fail (self, "protocol-error", "channel has invalid \"flow-control\" option");
    }
}
Beispiel #6
0
static void
cockpit_fslist_prepare (CockpitChannel *channel)
{
  CockpitFslist *self = COCKPIT_FSLIST (channel);
  const gchar *problem = "protocol-error";
  JsonObject *options;
  GError *error = NULL;
  GFile *file = NULL;
  gboolean watch;

  COCKPIT_CHANNEL_CLASS (cockpit_fslist_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 fslist1 channel");
      goto out;
    }
  if (self->path == NULL || *(self->path) == 0)
    {
      g_warning ("missing \"path\" option for fslist1 channel");
      goto out;
    }

  if (!cockpit_json_get_bool (options, "watch", TRUE, &watch))
    {
      g_warning ("invalid \"watch\" option for fslist1 channel");
      goto out;
    }

  self->cancellable = g_cancellable_new ();

  file = g_file_new_for_path (self->path);
  g_file_enumerate_children_async (file,
                                   G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
                                   G_FILE_QUERY_INFO_NONE,
                                   G_PRIORITY_DEFAULT,
                                   self->cancellable,
                                   on_enumerator_ready,
                                   self);

  if (watch)
    {
      self->monitor = g_file_monitor_directory (file, 0, NULL, &error);
      if (self->monitor == NULL)
        {
          g_message ("%s: couldn't monitor directory: %s", self->path, error->message);
          options = cockpit_channel_close_options (channel);
          json_object_set_string_member (options, "message", error->message);
          problem = "internal-error";
          goto out;
        }

      self->sig_changed = g_signal_connect (self->monitor, "changed", G_CALLBACK (on_changed), self);
    }

  cockpit_channel_ready (channel);
  problem = NULL;

out:
  g_clear_error (&error);
  if (file)
    g_object_unref (file);
  if (problem)
    cockpit_channel_close (channel, problem);
}
Beispiel #7
0
CockpitStreamOptions *
cockpit_channel_parse_stream (CockpitChannel *self,
                              gboolean local_address)
{
  const gchar *problem = "protocol-error";
  GTlsCertificate *cert = NULL;
  GTlsDatabase *database = NULL;
  CockpitStreamOptions *ret;
  gboolean use_tls = FALSE;
  GError *error = NULL;
  GString *pem = NULL;
  JsonObject *options;
  JsonNode *node;

  /* No validation for local servers by default */
  gboolean validate = !local_address;

  node = json_object_get_member (self->priv->open_options, "tls");
  if (node && !JSON_NODE_HOLDS_OBJECT (node))
    {
      g_warning ("invalid \"tls\" option for channel");
      goto out;
    }
  else if (node)
    {
      options = json_node_get_object (node);
      use_tls = TRUE;

      /*
       * The only function in GLib to parse private keys takes
       * them in PEM concatenated form. This is a limitation of GLib,
       * rather than concatenated form being a decent standard for
       * certificates and keys. So build a combined PEM as expected by
       * GLib here.
       */

      pem = g_string_sized_new (8192);

      problem = parse_cert_option_as_pem (options, "certificate", pem);
      if (problem)
        goto out;

      if (pem->len)
        {
          problem = parse_cert_option_as_pem (options, "key", pem);
          if (problem)
            goto out;

          cert = g_tls_certificate_new_from_pem (pem->str, pem->len, &error);
          if (error != NULL)
            {
              g_warning ("invalid \"certificate\" or \"key\" content: %s", error->message);
              g_error_free (error);
              problem = "internal-error";
              goto out;
            }
        }

      problem = parse_cert_option_as_database (options, "authority", &database);
      if (problem)
        goto out;

      if (!cockpit_json_get_bool (options, "validate", validate, &validate))
        {
          g_warning ("invalid \"validate\" option");
          problem = "protocol-error";
          goto out;
        }
    }

  problem = NULL;

out:
  if (problem)
    {
      cockpit_channel_close (self, problem);
      ret = NULL;
    }
  else
    {
      ret = g_new0 (CockpitStreamOptions, 1);
      ret->refs = 1;
      ret->tls_client = use_tls;
      ret->tls_cert = cert;
      cert = NULL;

      if (database)
        {
          ret->tls_database = database;
          ret->tls_client_flags = G_TLS_CERTIFICATE_VALIDATE_ALL;
          if (!validate)
              ret->tls_client_flags &= ~(G_TLS_CERTIFICATE_INSECURE | G_TLS_CERTIFICATE_BAD_IDENTITY);
          database = NULL;
        }
      else
        {
          if (validate)
            ret->tls_client_flags = G_TLS_CERTIFICATE_VALIDATE_ALL;
          else
            ret->tls_client_flags = G_TLS_CERTIFICATE_GENERIC_ERROR;
        }
    }

  if (pem)
    g_string_free (pem, TRUE);
  if (cert)
    g_object_unref (cert);
  if (database)
    g_object_unref (database);

  return ret;
}