(root)/
glib-2.79.0/
gio/
gdbusauthmechanismexternal.c
       1  /* GDBus - GLib D-Bus Library
       2   *
       3   * Copyright (C) 2008-2010 Red Hat, Inc.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   *
      20   * Author: David Zeuthen <davidz@redhat.com>
      21   */
      22  
      23  #include "config.h"
      24  
      25  #include <string.h>
      26  
      27  #include "gdbusauthmechanismexternal.h"
      28  #include "gcredentials.h"
      29  #include "gdbuserror.h"
      30  #include "gioenumtypes.h"
      31  
      32  #include "glibintl.h"
      33  
      34  #ifdef G_OS_WIN32
      35  #include "gwin32sid.h"
      36  #endif
      37  
      38  struct _GDBusAuthMechanismExternalPrivate
      39  {
      40    gboolean is_client;
      41    gboolean is_server;
      42    GDBusAuthMechanismState state;
      43    gboolean empty_data_sent;
      44  };
      45  
      46  static gint                     mechanism_get_priority              (void);
      47  static const gchar             *mechanism_get_name                  (void);
      48  
      49  static gboolean                 mechanism_is_supported              (GDBusAuthMechanism   *mechanism);
      50  static gchar                   *mechanism_encode_data               (GDBusAuthMechanism   *mechanism,
      51                                                                       const gchar          *data,
      52                                                                       gsize                 data_len,
      53                                                                       gsize                *out_data_len);
      54  static gchar                   *mechanism_decode_data               (GDBusAuthMechanism   *mechanism,
      55                                                                       const gchar          *data,
      56                                                                       gsize                 data_len,
      57                                                                       gsize                *out_data_len);
      58  static GDBusAuthMechanismState  mechanism_server_get_state          (GDBusAuthMechanism   *mechanism);
      59  static void                     mechanism_server_initiate           (GDBusAuthMechanism   *mechanism,
      60                                                                       const gchar          *initial_response,
      61                                                                       gsize                 initial_response_len);
      62  static void                     mechanism_server_data_receive       (GDBusAuthMechanism   *mechanism,
      63                                                                       const gchar          *data,
      64                                                                       gsize                 data_len);
      65  static gchar                   *mechanism_server_data_send          (GDBusAuthMechanism   *mechanism,
      66                                                                       gsize                *out_data_len);
      67  static gchar                   *mechanism_server_get_reject_reason  (GDBusAuthMechanism   *mechanism);
      68  static void                     mechanism_server_shutdown           (GDBusAuthMechanism   *mechanism);
      69  static GDBusAuthMechanismState  mechanism_client_get_state          (GDBusAuthMechanism   *mechanism);
      70  static gchar                   *mechanism_client_initiate           (GDBusAuthMechanism   *mechanism,
      71                                                                       GDBusConnectionFlags  conn_flags,
      72                                                                       gsize                *out_initial_response_len);
      73  static void                     mechanism_client_data_receive       (GDBusAuthMechanism   *mechanism,
      74                                                                       const gchar          *data,
      75                                                                       gsize                 data_len);
      76  static gchar                   *mechanism_client_data_send          (GDBusAuthMechanism   *mechanism,
      77                                                                       gsize                *out_data_len);
      78  static void                     mechanism_client_shutdown           (GDBusAuthMechanism   *mechanism);
      79  
      80  /* ---------------------------------------------------------------------------------------------------- */
      81  
      82  G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
      83  
      84  /* ---------------------------------------------------------------------------------------------------- */
      85  
      86  static void
      87  _g_dbus_auth_mechanism_external_finalize (GObject *object)
      88  {
      89    //GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object);
      90  
      91    if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL)
      92      G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object);
      93  }
      94  
      95  static void
      96  _g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass)
      97  {
      98    GObjectClass *gobject_class;
      99    GDBusAuthMechanismClass *mechanism_class;
     100  
     101    gobject_class = G_OBJECT_CLASS (klass);
     102    gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize;
     103  
     104    mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
     105    mechanism_class->get_name                  = mechanism_get_name;
     106    mechanism_class->get_priority              = mechanism_get_priority;
     107    mechanism_class->is_supported              = mechanism_is_supported;
     108    mechanism_class->encode_data               = mechanism_encode_data;
     109    mechanism_class->decode_data               = mechanism_decode_data;
     110    mechanism_class->server_get_state          = mechanism_server_get_state;
     111    mechanism_class->server_initiate           = mechanism_server_initiate;
     112    mechanism_class->server_data_receive       = mechanism_server_data_receive;
     113    mechanism_class->server_data_send          = mechanism_server_data_send;
     114    mechanism_class->server_get_reject_reason  = mechanism_server_get_reject_reason;
     115    mechanism_class->server_shutdown           = mechanism_server_shutdown;
     116    mechanism_class->client_get_state          = mechanism_client_get_state;
     117    mechanism_class->client_initiate           = mechanism_client_initiate;
     118    mechanism_class->client_data_receive       = mechanism_client_data_receive;
     119    mechanism_class->client_data_send          = mechanism_client_data_send;
     120    mechanism_class->client_shutdown           = mechanism_client_shutdown;
     121  }
     122  
     123  static void
     124  _g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
     125  {
     126    mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (mechanism);
     127  }
     128  
     129  /* ---------------------------------------------------------------------------------------------------- */
     130  
     131  static gboolean
     132  mechanism_is_supported (GDBusAuthMechanism *mechanism)
     133  {
     134    g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
     135  
     136  #if defined(G_OS_WIN32)
     137    /* all that is required is current process SID */
     138    return TRUE;
     139  #else
     140    /* This mechanism is only available if credentials has been exchanged */
     141    if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
     142      return TRUE;
     143    else
     144      return FALSE;
     145  #endif
     146  }
     147  
     148  static gint
     149  mechanism_get_priority (void)
     150  {
     151    /* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
     152    return 100;
     153  }
     154  
     155  static const gchar *
     156  mechanism_get_name (void)
     157  {
     158    return "EXTERNAL";
     159  }
     160  
     161  static gchar *
     162  mechanism_encode_data (GDBusAuthMechanism   *mechanism,
     163                         const gchar          *data,
     164                         gsize                 data_len,
     165                         gsize                *out_data_len)
     166  {
     167    return NULL;
     168  }
     169  
     170  
     171  static gchar *
     172  mechanism_decode_data (GDBusAuthMechanism   *mechanism,
     173                         const gchar          *data,
     174                         gsize                 data_len,
     175                         gsize                *out_data_len)
     176  {
     177    return NULL;
     178  }
     179  
     180  /* ---------------------------------------------------------------------------------------------------- */
     181  
     182  static GDBusAuthMechanismState
     183  mechanism_server_get_state (GDBusAuthMechanism   *mechanism)
     184  {
     185    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     186  
     187    g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
     188    g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
     189  
     190    return m->priv->state;
     191  }
     192  
     193  static gboolean
     194  data_matches_credentials (const gchar  *data,
     195                            gsize         data_len,
     196                            GCredentials *credentials)
     197  {
     198    gboolean match;
     199  
     200    match = FALSE;
     201  
     202    if (credentials == NULL)
     203      goto out;
     204  
     205  #if defined(G_OS_UNIX)
     206    {
     207      gint64 alleged_uid;
     208      gchar *endp;
     209  
     210      /* If we were unable to find out the uid, then nothing
     211       * can possibly match it.  */
     212      if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
     213        goto out;
     214  
     215      /* An empty authorization identity means we want to be
     216       * whatever identity the out-of-band credentials say we have
     217       * (RFC 4422 appendix A.1). This effectively matches any uid. */
     218      if (data == NULL || data_len == 0)
     219        {
     220          match = TRUE;
     221          goto out;
     222        }
     223      /* on UNIX, this is the uid as a string in base 10 */
     224      alleged_uid = g_ascii_strtoll (data, &endp, 10);
     225      if (*endp == '\0')
     226        {
     227          if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid)
     228            {
     229              match = TRUE;
     230            }
     231        }
     232    }
     233  #else
     234    /* TODO: Dont know how to compare credentials on this OS. Please implement. */
     235  #endif
     236  
     237   out:
     238    return match;
     239  }
     240  
     241  static void
     242  mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
     243                             const gchar          *initial_response,
     244                             gsize                 initial_response_len)
     245  {
     246    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     247  
     248    g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
     249    g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
     250  
     251    m->priv->is_server = TRUE;
     252  
     253    if (initial_response != NULL)
     254      {
     255        if (data_matches_credentials (initial_response,
     256                                      initial_response_len,
     257                                      _g_dbus_auth_mechanism_get_credentials (mechanism)))
     258          {
     259            m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
     260          }
     261        else
     262          {
     263            m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
     264          }
     265      }
     266    else
     267      {
     268        /* The initial-response optimization was not used, so we need to
     269         * send an empty challenge to prompt the client to respond. */
     270        m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
     271      }
     272  }
     273  
     274  static void
     275  mechanism_server_data_receive (GDBusAuthMechanism   *mechanism,
     276                                 const gchar          *data,
     277                                 gsize                 data_len)
     278  {
     279    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     280  
     281    g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
     282    g_return_if_fail (m->priv->is_server && !m->priv->is_client);
     283    g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
     284  
     285    if (data_matches_credentials (data,
     286                                  data_len,
     287                                  _g_dbus_auth_mechanism_get_credentials (mechanism)))
     288      {
     289        m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
     290      }
     291    else
     292      {
     293        m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
     294      }
     295  }
     296  
     297  static gchar *
     298  mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
     299                              gsize                *out_data_len)
     300  {
     301    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     302  
     303    g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
     304    g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
     305  
     306    if (out_data_len)
     307      *out_data_len = 0;
     308  
     309    if (m->priv->empty_data_sent)
     310      {
     311        /* We have already sent an empty data response.
     312           Reject the connection.  */
     313        m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
     314        return NULL;
     315      }
     316  
     317    m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
     318    m->priv->empty_data_sent = TRUE;
     319  
     320    return g_strdup ("");
     321  }
     322  
     323  static gchar *
     324  mechanism_server_get_reject_reason (GDBusAuthMechanism   *mechanism)
     325  {
     326    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     327  
     328    g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
     329    g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
     330    g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
     331  
     332    /* can never end up here because we are never in the REJECTED state */
     333    g_assert_not_reached ();
     334  
     335    return NULL;
     336  }
     337  
     338  static void
     339  mechanism_server_shutdown (GDBusAuthMechanism   *mechanism)
     340  {
     341    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     342  
     343    g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
     344    g_return_if_fail (m->priv->is_server && !m->priv->is_client);
     345  
     346    m->priv->is_server = FALSE;
     347  }
     348  
     349  /* ---------------------------------------------------------------------------------------------------- */
     350  
     351  static GDBusAuthMechanismState
     352  mechanism_client_get_state (GDBusAuthMechanism   *mechanism)
     353  {
     354    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     355  
     356    g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
     357    g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
     358  
     359    return m->priv->state;
     360  }
     361  
     362  static gchar *
     363  mechanism_client_initiate (GDBusAuthMechanism   *mechanism,
     364                             GDBusConnectionFlags  conn_flags,
     365                             gsize                *out_initial_response_len)
     366  {
     367    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     368    gchar *initial_response = NULL;
     369  
     370    g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
     371    g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
     372  
     373    m->priv->is_client = TRUE;
     374    m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
     375  
     376    *out_initial_response_len = 0;
     377  
     378    if (conn_flags & G_DBUS_CONNECTION_FLAGS_CROSS_NAMESPACE)
     379      {
     380        /* If backwards-compatibility with GDBus servers < 2.73.3 is not a
     381         * concern, we do not send an initial response, because there is
     382         * no way to express an empty authorization identity this way.
     383         * Instead, we'll reply to the server's first (empty) challenge
     384         * with an empty authorization identity in our first response.  */
     385        g_debug ("Using cross-namespace EXTERNAL authentication (this will deadlock if server is GDBus < 2.73.3)");
     386      }
     387    else
     388      {
     389        /* Send the Unix uid or Windows SID as an initial response.
     390         * This is the only thing that is interoperable with GDBus 2.73.3
     391         * servers. */
     392  #if defined(G_OS_UNIX)
     393        GCredentials *credentials;
     394  
     395        credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
     396        g_assert (credentials != NULL);
     397  
     398        initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
     399  #elif defined(G_OS_WIN32)
     400        initial_response = _g_win32_current_process_sid_string (NULL);
     401  #else
     402        /* GDBus < 2.73.3 servers can't have worked on this platform anyway,
     403         * so it isn't a regression to behave as though
     404         * G_DBUS_CONNECTION_FLAGS_CROSS_NAMESPACE had been set. */
     405        g_debug ("Unknown platform, cannot use initial response in EXTERNAL");
     406  #endif
     407      }
     408  
     409    if (initial_response)
     410      {
     411        m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
     412        *out_initial_response_len = strlen (initial_response);
     413      }
     414    return initial_response;
     415  }
     416  
     417  static void
     418  mechanism_client_data_receive (GDBusAuthMechanism   *mechanism,
     419                                 const gchar          *data,
     420                                 gsize                 data_len)
     421  {
     422    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     423  
     424    g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
     425    g_return_if_fail (m->priv->is_client && !m->priv->is_server);
     426    g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
     427  
     428    /* The server sent us a challenge, which should normally
     429     * be empty.  We respond with our authorization identity.  */
     430    m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
     431  }
     432  
     433  static gchar *
     434  mechanism_client_data_send (GDBusAuthMechanism   *mechanism,
     435                              gsize                *out_data_len)
     436  {
     437    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     438  
     439    g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
     440    g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
     441    g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
     442  
     443    /* We respond to the server's challenge by sending our
     444     * authorization identity, which is the empty string, meaning
     445     * whoever the out-of-band credentials say we are.  */
     446    *out_data_len = 0;
     447    return g_strdup ("");
     448  }
     449  
     450  static void
     451  mechanism_client_shutdown (GDBusAuthMechanism   *mechanism)
     452  {
     453    GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
     454  
     455    g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
     456    g_return_if_fail (m->priv->is_client && !m->priv->is_server);
     457  
     458    m->priv->is_client = FALSE;
     459  }
     460  
     461  /* ---------------------------------------------------------------------------------------------------- */