LCOV - code coverage report
Current view: top level - gio - gdbusauthmechanismsha1.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 76.7 % 493 378
Test Date: 2026-06-09 05:14:17 Functions: 88.6 % 35 31
Branches: - 0 0

             Branch data     Line data    Source code
       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                 :             : #include <fcntl.h>
      27                 :             : #include <errno.h>
      28                 :             : #include <sys/types.h>
      29                 :             : 
      30                 :             : #include <glib/gstdio.h>
      31                 :             : 
      32                 :             : #ifdef G_OS_UNIX
      33                 :             : #include <unistd.h>
      34                 :             : #endif
      35                 :             : #ifdef G_OS_WIN32
      36                 :             : #include <io.h>
      37                 :             : #include "gwin32sid.h"
      38                 :             : #endif
      39                 :             : 
      40                 :             : #ifndef O_CLOEXEC
      41                 :             : #define O_CLOEXEC 0
      42                 :             : #endif
      43                 :             : 
      44                 :             : #include "gdbusauthmechanismsha1.h"
      45                 :             : #include "gcredentials.h"
      46                 :             : #include "gdbuserror.h"
      47                 :             : #include "glocalfileinfo.h"
      48                 :             : #include "gioenumtypes.h"
      49                 :             : #include "gioerror.h"
      50                 :             : #include "gdbusprivate.h"
      51                 :             : #include "glib-private.h"
      52                 :             : 
      53                 :             : #include "glibintl.h"
      54                 :             : 
      55                 :             : /*
      56                 :             :  * Arbitrary timeouts for keys in the keyring.
      57                 :             :  * For interoperability, these match the reference implementation, libdbus.
      58                 :             :  * To make them easier to compare, their names also match libdbus
      59                 :             :  * (see dbus/dbus-keyring.c).
      60                 :             :  */
      61                 :             : 
      62                 :             : /*
      63                 :             :  * Maximum age of a key before we create a new key to use in challenges:
      64                 :             :  * 5 minutes.
      65                 :             :  */
      66                 :             : #define NEW_KEY_TIMEOUT_SECONDS (60*5)
      67                 :             : 
      68                 :             : /*
      69                 :             :  * Time before we drop a key from the keyring: 7 minutes.
      70                 :             :  * Authentication will succeed if it takes less than
      71                 :             :  * EXPIRE_KEYS_TIMEOUT_SECONDS - NEW_KEY_TIMEOUT_SECONDS (2 minutes)
      72                 :             :  * to complete.
      73                 :             :  * The spec says "delete any cookies that are old (the timeout can be
      74                 :             :  * fairly short)".
      75                 :             :  */
      76                 :             : #define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2))
      77                 :             : 
      78                 :             : /*
      79                 :             :  * Maximum amount of time a key can be in the future due to clock skew
      80                 :             :  * with a shared home directory: 5 minutes.
      81                 :             :  * The spec says "a reasonable time in the future".
      82                 :             :  */
      83                 :             : #define MAX_TIME_TRAVEL_SECONDS (60*5)
      84                 :             : 
      85                 :             : 
      86                 :             : struct _GDBusAuthMechanismSha1Private
      87                 :             : {
      88                 :             :   gboolean is_client;
      89                 :             :   gboolean is_server;
      90                 :             :   GDBusAuthMechanismState state;
      91                 :             :   gchar *reject_reason;  /* non-NULL iff (state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED) */
      92                 :             : 
      93                 :             :   /* used on the client side */
      94                 :             :   gchar *to_send;
      95                 :             : 
      96                 :             :   /* used on the server side */
      97                 :             :   gchar *cookie;
      98                 :             :   gchar *server_challenge;
      99                 :             : };
     100                 :             : 
     101                 :             : static gint                     mechanism_get_priority              (void);
     102                 :             : static const gchar             *mechanism_get_name                  (void);
     103                 :             : 
     104                 :             : static gboolean                 mechanism_is_supported              (GDBusAuthMechanism   *mechanism);
     105                 :             : static gchar                   *mechanism_encode_data               (GDBusAuthMechanism   *mechanism,
     106                 :             :                                                                      const gchar          *data,
     107                 :             :                                                                      gsize                 data_len,
     108                 :             :                                                                      gsize                *out_data_len);
     109                 :             : static gchar                   *mechanism_decode_data               (GDBusAuthMechanism   *mechanism,
     110                 :             :                                                                      const gchar          *data,
     111                 :             :                                                                      gsize                 data_len,
     112                 :             :                                                                      gsize                *out_data_len);
     113                 :             : static GDBusAuthMechanismState  mechanism_server_get_state          (GDBusAuthMechanism   *mechanism);
     114                 :             : static void                     mechanism_server_initiate           (GDBusAuthMechanism   *mechanism,
     115                 :             :                                                                      const gchar          *initial_response,
     116                 :             :                                                                      gsize                 initial_response_len);
     117                 :             : static void                     mechanism_server_data_receive       (GDBusAuthMechanism   *mechanism,
     118                 :             :                                                                      const gchar          *data,
     119                 :             :                                                                      gsize                 data_len);
     120                 :             : static gchar                   *mechanism_server_data_send          (GDBusAuthMechanism   *mechanism,
     121                 :             :                                                                      gsize                *out_data_len);
     122                 :             : static gchar                   *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism   *mechanism);
     123                 :             : static void                     mechanism_server_shutdown           (GDBusAuthMechanism   *mechanism);
     124                 :             : static GDBusAuthMechanismState  mechanism_client_get_state          (GDBusAuthMechanism   *mechanism);
     125                 :             : static gchar                   *mechanism_client_initiate           (GDBusAuthMechanism   *mechanism,
     126                 :             :                                                                      GDBusConnectionFlags  conn_flags,
     127                 :             :                                                                      gsize                *out_initial_response_len);
     128                 :             : static void                     mechanism_client_data_receive       (GDBusAuthMechanism   *mechanism,
     129                 :             :                                                                      const gchar          *data,
     130                 :             :                                                                      gsize                 data_len);
     131                 :             : static gchar                   *mechanism_client_data_send          (GDBusAuthMechanism   *mechanism,
     132                 :             :                                                                      gsize                *out_data_len);
     133                 :             : static void                     mechanism_client_shutdown           (GDBusAuthMechanism   *mechanism);
     134                 :             : 
     135                 :             : /* ---------------------------------------------------------------------------------------------------- */
     136                 :             : 
     137                 :        4753 : G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismSha1, _g_dbus_auth_mechanism_sha1, G_TYPE_DBUS_AUTH_MECHANISM)
     138                 :             : 
     139                 :             : /* ---------------------------------------------------------------------------------------------------- */
     140                 :             : 
     141                 :             : static void
     142                 :          92 : _g_dbus_auth_mechanism_sha1_finalize (GObject *object)
     143                 :             : {
     144                 :          92 :   GDBusAuthMechanismSha1 *mechanism = G_DBUS_AUTH_MECHANISM_SHA1 (object);
     145                 :             : 
     146                 :          92 :   g_free (mechanism->priv->reject_reason);
     147                 :          92 :   g_free (mechanism->priv->to_send);
     148                 :             : 
     149                 :          92 :   g_free (mechanism->priv->cookie);
     150                 :          92 :   g_free (mechanism->priv->server_challenge);
     151                 :             : 
     152                 :          92 :   if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize != NULL)
     153                 :          92 :     G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize (object);
     154                 :          92 : }
     155                 :             : 
     156                 :             : static void
     157                 :         101 : _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass)
     158                 :             : {
     159                 :             :   GObjectClass *gobject_class;
     160                 :             :   GDBusAuthMechanismClass *mechanism_class;
     161                 :             : 
     162                 :         101 :   gobject_class = G_OBJECT_CLASS (klass);
     163                 :         101 :   gobject_class->finalize = _g_dbus_auth_mechanism_sha1_finalize;
     164                 :             : 
     165                 :         101 :   mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
     166                 :         101 :   mechanism_class->get_priority              = mechanism_get_priority;
     167                 :         101 :   mechanism_class->get_name                  = mechanism_get_name;
     168                 :         101 :   mechanism_class->is_supported              = mechanism_is_supported;
     169                 :         101 :   mechanism_class->encode_data               = mechanism_encode_data;
     170                 :         101 :   mechanism_class->decode_data               = mechanism_decode_data;
     171                 :         101 :   mechanism_class->server_get_state          = mechanism_server_get_state;
     172                 :         101 :   mechanism_class->server_initiate           = mechanism_server_initiate;
     173                 :         101 :   mechanism_class->server_data_receive       = mechanism_server_data_receive;
     174                 :         101 :   mechanism_class->server_data_send          = mechanism_server_data_send;
     175                 :         101 :   mechanism_class->server_get_reject_reason  = mechanism_server_or_client_get_reject_reason;
     176                 :         101 :   mechanism_class->server_shutdown           = mechanism_server_shutdown;
     177                 :         101 :   mechanism_class->client_get_state          = mechanism_client_get_state;
     178                 :         101 :   mechanism_class->client_initiate           = mechanism_client_initiate;
     179                 :         101 :   mechanism_class->client_data_receive       = mechanism_client_data_receive;
     180                 :         101 :   mechanism_class->client_data_send          = mechanism_client_data_send;
     181                 :         101 :   mechanism_class->client_get_reject_reason  = mechanism_server_or_client_get_reject_reason;
     182                 :         101 :   mechanism_class->client_shutdown           = mechanism_client_shutdown;
     183                 :         101 : }
     184                 :             : 
     185                 :             : static void
     186                 :          92 : _g_dbus_auth_mechanism_sha1_init (GDBusAuthMechanismSha1 *mechanism)
     187                 :             : {
     188                 :          92 :   mechanism->priv = _g_dbus_auth_mechanism_sha1_get_instance_private (mechanism);
     189                 :          92 : }
     190                 :             : 
     191                 :             : /* ---------------------------------------------------------------------------------------------------- */
     192                 :             : 
     193                 :             : static gint
     194                 :        3200 : mechanism_get_priority (void)
     195                 :             : {
     196                 :        3200 :   return 0;
     197                 :             : }
     198                 :             : 
     199                 :             : static const gchar *
     200                 :        3314 : mechanism_get_name (void)
     201                 :             : {
     202                 :        3314 :   return "DBUS_COOKIE_SHA1";
     203                 :             : }
     204                 :             : 
     205                 :             : static gboolean
     206                 :          24 : mechanism_is_supported (GDBusAuthMechanism *mechanism)
     207                 :             : {
     208                 :          24 :   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), FALSE);
     209                 :          24 :   return TRUE;
     210                 :             : }
     211                 :             : 
     212                 :             : static gchar *
     213                 :           0 : mechanism_encode_data (GDBusAuthMechanism   *mechanism,
     214                 :             :                        const gchar          *data,
     215                 :             :                        gsize                 data_len,
     216                 :             :                        gsize                *out_data_len)
     217                 :             : {
     218                 :           0 :   return NULL;
     219                 :             : }
     220                 :             : 
     221                 :             : 
     222                 :             : static gchar *
     223                 :           0 : mechanism_decode_data (GDBusAuthMechanism   *mechanism,
     224                 :             :                        const gchar          *data,
     225                 :             :                        gsize                 data_len,
     226                 :             :                        gsize                *out_data_len)
     227                 :             : {
     228                 :           0 :   return NULL;
     229                 :             : }
     230                 :             : 
     231                 :             : /* ---------------------------------------------------------------------------------------------------- */
     232                 :             : 
     233                 :             : static gint
     234                 :        1232 : random_ascii (void)
     235                 :             : {
     236                 :             :   gint ret;
     237                 :        1232 :   ret = g_random_int_range (0, 60);
     238                 :        1232 :   if (ret < 25)
     239                 :         498 :     ret += 'A';
     240                 :         734 :   else if (ret < 50)
     241                 :         529 :     ret += 'a' - 25;
     242                 :             :   else
     243                 :         205 :     ret += '0' - 50;
     244                 :        1232 :   return ret;
     245                 :             : }
     246                 :             : 
     247                 :             : static gchar *
     248                 :          77 : random_ascii_string (guint len)
     249                 :             : {
     250                 :             :   GString *challenge;
     251                 :             :   guint n;
     252                 :             : 
     253                 :          77 :   challenge = g_string_new (NULL);
     254                 :        1309 :   for (n = 0; n < len; n++)
     255                 :        1232 :     g_string_append_c (challenge, random_ascii ());
     256                 :          77 :   return g_string_free (challenge, FALSE);
     257                 :             : }
     258                 :             : 
     259                 :             : static gchar *
     260                 :           7 : random_blob (guint len)
     261                 :             : {
     262                 :             :   GString *challenge;
     263                 :             :   guint n;
     264                 :             : 
     265                 :           7 :   challenge = g_string_new (NULL);
     266                 :         231 :   for (n = 0; n < len; n++)
     267                 :         224 :     g_string_append_c (challenge, g_random_int_range (0, 256));
     268                 :           7 :   return g_string_free (challenge, FALSE);
     269                 :             : }
     270                 :             : 
     271                 :             : /* ---------------------------------------------------------------------------------------------------- */
     272                 :             : 
     273                 :             : /* ensure keyring dir exists and permissions are correct */
     274                 :             : static gchar *
     275                 :          78 : ensure_keyring_directory (GError **error)
     276                 :             : {
     277                 :             :   gchar *path;
     278                 :             :   const gchar *e;
     279                 :             : #ifdef G_OS_UNIX
     280                 :             :   struct stat statbuf;
     281                 :             : #endif
     282                 :             : 
     283                 :          78 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     284                 :             : 
     285                 :             :   /* If running as setuid, we can’t trust the environment (as it’s provided by
     286                 :             :    * a user running at a lower privilege level), so we can’t check
     287                 :             :    * $G_DBUS_COOKIE_SHA1_KEYRING_DIR *and* we can’t build it using $HOME.
     288                 :             :    * Just bail out in that case. */
     289                 :          78 :   if (GLIB_PRIVATE_CALL (g_check_setuid) ())
     290                 :             :     {
     291                 :           0 :       g_set_error_literal (error,
     292                 :             :                            G_IO_ERROR,
     293                 :             :                            G_IO_ERROR_PERMISSION_DENIED,
     294                 :             :                            _("Error accessing D-Bus keyring: keyring-based "
     295                 :             :                              "authentication is not supported when running as setuid"));
     296                 :           0 :       return NULL;
     297                 :             :     }
     298                 :             : 
     299                 :          78 :   e = g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR");
     300                 :          78 :   if (e != NULL)
     301                 :             :     {
     302                 :           4 :       path = g_strdup (e);
     303                 :             :     }
     304                 :             :   else
     305                 :             :     {
     306                 :          74 :       path = g_build_filename (g_get_home_dir (),
     307                 :             :                                ".dbus-keyrings",
     308                 :             :                                NULL);
     309                 :             :     }
     310                 :             : 
     311                 :             : #ifdef G_OS_UNIX
     312                 :          78 :   if (stat (path, &statbuf) != 0)
     313                 :             :     {
     314                 :           7 :       int errsv = errno;
     315                 :             : 
     316                 :           7 :       if (errsv != ENOENT)
     317                 :             :         {
     318                 :           0 :           g_set_error (error,
     319                 :             :                        G_IO_ERROR,
     320                 :           0 :                        g_io_error_from_errno (errsv),
     321                 :             :                        _("Error when getting information for directory “%s”: %s"),
     322                 :             :                        path,
     323                 :             :                        g_strerror (errsv));
     324                 :           0 :           g_clear_pointer (&path, g_free);
     325                 :           0 :           return NULL;
     326                 :             :         }
     327                 :             :     }
     328                 :          71 :   else if (S_ISDIR (statbuf.st_mode))
     329                 :             :     {
     330                 :          71 :       if (g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION") == NULL &&
     331                 :          67 :           (statbuf.st_mode & 0777) != 0700)
     332                 :             :         {
     333                 :           0 :           g_set_error (error,
     334                 :             :                        G_IO_ERROR,
     335                 :             :                        G_IO_ERROR_FAILED,
     336                 :             :                        _("Permissions on directory “%s” are malformed. Expected mode 0700, got 0%o"),
     337                 :             :                        path,
     338                 :           0 :                        (guint) (statbuf.st_mode & 0777));
     339                 :           0 :           g_clear_pointer (&path, g_free);
     340                 :           0 :           return NULL;
     341                 :             :         }
     342                 :             : 
     343                 :          71 :       return g_steal_pointer (&path);
     344                 :             :     }
     345                 :             : #else  /* if !G_OS_UNIX */
     346                 :             :   /* On non-Unix platforms, check that it exists as a directory, but don’t do
     347                 :             :    * permissions checks at the moment. */
     348                 :             :   if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
     349                 :             :     {
     350                 :             : #ifdef __GNUC__
     351                 :             : #pragma GCC diagnostic push
     352                 :             : #pragma GCC diagnostic warning "-Wcpp"
     353                 :             : #warning Please implement permission checking on this non-UNIX platform
     354                 :             : #pragma GCC diagnostic pop
     355                 :             : #endif  /* __GNUC__ */
     356                 :             :       return g_steal_pointer (&path);
     357                 :             :     }
     358                 :             : #endif  /* if !G_OS_UNIX */
     359                 :             : 
     360                 :             :   /* Only create the directory if not running as setuid */
     361                 :           7 :   if (g_mkdir_with_parents (path, 0700) != 0)
     362                 :             :     {
     363                 :           0 :       int errsv = errno;
     364                 :           0 :       g_set_error (error,
     365                 :             :                    G_IO_ERROR,
     366                 :           0 :                    g_io_error_from_errno (errsv),
     367                 :             :                    _("Error creating directory “%s”: %s"),
     368                 :             :                    path,
     369                 :             :                    g_strerror (errsv));
     370                 :           0 :       g_clear_pointer (&path, g_free);
     371                 :           0 :       return NULL;
     372                 :             :     }
     373                 :             : 
     374                 :           7 :   return g_steal_pointer (&path);
     375                 :             : }
     376                 :             : 
     377                 :             : /* ---------------------------------------------------------------------------------------------------- */
     378                 :             : 
     379                 :             : /* looks up an entry in the keyring */
     380                 :             : static gchar *
     381                 :          10 : keyring_lookup_entry (const gchar  *cookie_context,
     382                 :             :                       gint          cookie_id,
     383                 :             :                       GError      **error)
     384                 :             : {
     385                 :             :   gchar *ret;
     386                 :             :   gchar *keyring_dir;
     387                 :             :   gchar *contents;
     388                 :             :   gchar *path;
     389                 :             :   guint n;
     390                 :             :   gchar **lines;
     391                 :             : 
     392                 :          10 :   g_return_val_if_fail (cookie_context != NULL, NULL);
     393                 :          10 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     394                 :             : 
     395                 :          10 :   ret = NULL;
     396                 :          10 :   path = NULL;
     397                 :          10 :   contents = NULL;
     398                 :          10 :   lines = NULL;
     399                 :             : 
     400                 :          10 :   keyring_dir = ensure_keyring_directory (error);
     401                 :          10 :   if (keyring_dir == NULL)
     402                 :           0 :     goto out;
     403                 :             : 
     404                 :          10 :   path = g_build_filename (keyring_dir, cookie_context, NULL);
     405                 :             : 
     406                 :          10 :   if (!g_file_get_contents (path,
     407                 :             :                             &contents,
     408                 :             :                             NULL,
     409                 :             :                             error))
     410                 :             :     {
     411                 :           1 :       g_prefix_error (error,
     412                 :             :                       _("Error opening keyring “%s” for reading: "),
     413                 :             :                       path);
     414                 :           1 :       goto out;
     415                 :             :     }
     416                 :           9 :   g_assert (contents != NULL);
     417                 :             : 
     418                 :           9 :   lines = g_strsplit (contents, "\n", 0);
     419                 :           9 :   for (n = 0; lines[n] != NULL; n++)
     420                 :             :     {
     421                 :           9 :       const gchar *line = lines[n];
     422                 :             :       gchar **tokens;
     423                 :             :       gchar *endp;
     424                 :             :       gint line_id;
     425                 :             : 
     426                 :           9 :       if (line[0] == '\0')
     427                 :           0 :         continue;
     428                 :             : 
     429                 :           9 :       tokens = g_strsplit (line, " ", 0);
     430                 :           9 :       if (g_strv_length (tokens) != 3)
     431                 :             :         {
     432                 :           0 :           g_set_error (error,
     433                 :             :                        G_IO_ERROR,
     434                 :             :                        G_IO_ERROR_FAILED,
     435                 :             :                        _("Line %d of the keyring at “%s” with content “%s” is malformed"),
     436                 :             :                        n + 1,
     437                 :             :                        path,
     438                 :             :                        line);
     439                 :           0 :           g_strfreev (tokens);
     440                 :           9 :           goto out;
     441                 :             :         }
     442                 :             : 
     443                 :           9 :       line_id = g_ascii_strtoll (tokens[0], &endp, 10);
     444                 :           9 :       if (*endp != '\0')
     445                 :             :         {
     446                 :           0 :           g_set_error (error,
     447                 :             :                        G_IO_ERROR,
     448                 :             :                        G_IO_ERROR_FAILED,
     449                 :             :                        _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
     450                 :             :                        n + 1,
     451                 :             :                        path,
     452                 :             :                        line);
     453                 :           0 :           g_strfreev (tokens);
     454                 :           0 :           goto out;
     455                 :             :         }
     456                 :             : 
     457                 :           9 :       (void)g_ascii_strtoll (tokens[1], &endp, 10); /* do not care what the timestamp is */
     458                 :           9 :       if (*endp != '\0')
     459                 :             :         {
     460                 :           0 :           g_set_error (error,
     461                 :             :                        G_IO_ERROR,
     462                 :             :                        G_IO_ERROR_FAILED,
     463                 :             :                        _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
     464                 :             :                        n + 1,
     465                 :             :                        path,
     466                 :             :                        line);
     467                 :           0 :           g_strfreev (tokens);
     468                 :           0 :           goto out;
     469                 :             :         }
     470                 :             : 
     471                 :           9 :       if (line_id == cookie_id)
     472                 :             :         {
     473                 :             :           /* YAY, success */
     474                 :           9 :           ret = tokens[2]; /* steal pointer */
     475                 :           9 :           tokens[2] = NULL;
     476                 :           9 :           g_strfreev (tokens);
     477                 :           9 :           goto out;
     478                 :             :         }
     479                 :             : 
     480                 :           0 :       g_strfreev (tokens);
     481                 :             :     }
     482                 :             : 
     483                 :             :   /* BOOH, didn't find the cookie */
     484                 :           0 :   g_set_error (error,
     485                 :             :                G_IO_ERROR,
     486                 :             :                G_IO_ERROR_FAILED,
     487                 :             :                _("Didn’t find cookie with id %d in the keyring at “%s”"),
     488                 :             :                cookie_id,
     489                 :             :                path);
     490                 :             : 
     491                 :          10 :  out:
     492                 :          10 :   g_free (keyring_dir);
     493                 :          10 :   g_free (path);
     494                 :          10 :   g_free (contents);
     495                 :          10 :   g_strfreev (lines);
     496                 :          10 :   return ret;
     497                 :             : }
     498                 :             : 
     499                 :             : /* function for logging important events that the system administrator should take notice of */
     500                 :             : G_GNUC_PRINTF(1, 2)
     501                 :             : static void
     502                 :           0 : _log (const gchar *message,
     503                 :             :       ...)
     504                 :             : {
     505                 :             :   gchar *s;
     506                 :             :   va_list var_args;
     507                 :             : 
     508                 :           0 :   va_start (var_args, message);
     509                 :           0 :   s = g_strdup_vprintf (message, var_args);
     510                 :           0 :   va_end (var_args);
     511                 :             : 
     512                 :             :   /* TODO: might want to send this to syslog instead */
     513                 :           0 :   g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s);
     514                 :           0 :   g_free (s);
     515                 :           0 : }
     516                 :             : 
     517                 :             : /* Returns FD for lock file, if it was created exclusively (didn't exist already,
     518                 :             :  * and was created successfully) */
     519                 :             : static gint
     520                 :          68 : create_lock_exclusive (const gchar  *lock_path,
     521                 :             :                        gint64       *mtime_nsec,
     522                 :             :                        GError      **error)
     523                 :             : {
     524                 :             :   int errsv;
     525                 :             :   gint ret;
     526                 :             : 
     527                 :          68 :   ret = g_open (lock_path, O_CREAT | O_EXCL | O_CLOEXEC, 0600);
     528                 :          68 :   errsv = errno;
     529                 :          68 :   if (ret < 0)
     530                 :             :     {
     531                 :             :       GLocalFileStat stat_buf;
     532                 :             : 
     533                 :             :       /* Get the modification time to distinguish between the lock being stale
     534                 :             :        * or highly contested. */
     535                 :           0 :       if (mtime_nsec != NULL &&
     536                 :           0 :           g_local_file_stat (lock_path, G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, &stat_buf) == 0)
     537                 :           0 :         *mtime_nsec = _g_stat_mtime (&stat_buf) * G_USEC_PER_SEC * 1000 + _g_stat_mtim_nsec (&stat_buf);
     538                 :           0 :       else if (mtime_nsec != NULL)
     539                 :           0 :         *mtime_nsec = 0;
     540                 :             : 
     541                 :           0 :       g_set_error (error,
     542                 :             :                    G_IO_ERROR,
     543                 :           0 :                    g_io_error_from_errno (errsv),
     544                 :             :                    _("Error creating lock file “%s”: %s"),
     545                 :             :                    lock_path,
     546                 :             :                    g_strerror (errsv));
     547                 :           0 :       return -1;
     548                 :             :     }
     549                 :             : 
     550                 :          68 :   return ret;
     551                 :             : }
     552                 :             : 
     553                 :             : static gint
     554                 :          68 : keyring_acquire_lock (const gchar  *path,
     555                 :             :                       GError      **error)
     556                 :             : {
     557                 :          68 :   gchar *lock = NULL;
     558                 :             :   gint ret;
     559                 :             :   guint num_tries;
     560                 :             :   int errsv;
     561                 :          68 :   gint64 lock_mtime_nsec = 0, lock_mtime_nsec_prev = 0;
     562                 :             : 
     563                 :             :   /* Total possible sleep period = max_tries * timeout_usec = 0.5s */
     564                 :          68 :   const guint max_tries = 50;
     565                 :          68 :   const guint timeout_usec = 1000 * 10;
     566                 :             : 
     567                 :          68 :   g_return_val_if_fail (path != NULL, -1);
     568                 :          68 :   g_return_val_if_fail (error == NULL || *error == NULL, -1);
     569                 :             : 
     570                 :          68 :   ret = -1;
     571                 :          68 :   lock = g_strconcat (path, ".lock", NULL);
     572                 :             : 
     573                 :             :   /* This is what the D-Bus spec says
     574                 :             :    * (https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha)
     575                 :             :    *
     576                 :             :    *  Create a lockfile name by appending ".lock" to the name of the
     577                 :             :    *  cookie file. The server should attempt to create this file using
     578                 :             :    *  O_CREAT | O_EXCL. If file creation fails, the lock
     579                 :             :    *  fails. Servers should retry for a reasonable period of time,
     580                 :             :    *  then they may choose to delete an existing lock to keep users
     581                 :             :    *  from having to manually delete a stale lock. [1]
     582                 :             :    *
     583                 :             :    *  [1] : Lockfiles are used instead of real file locking fcntl() because
     584                 :             :    *         real locking implementations are still flaky on network filesystems
     585                 :             :    */
     586                 :             : 
     587                 :          68 :   for (num_tries = 0; num_tries < max_tries; num_tries++)
     588                 :             :     {
     589                 :          68 :       lock_mtime_nsec_prev = lock_mtime_nsec;
     590                 :             : 
     591                 :             :       /* Ignore the error until the final call. */
     592                 :          68 :       ret = create_lock_exclusive (lock, &lock_mtime_nsec, NULL);
     593                 :          68 :       if (ret >= 0)
     594                 :          68 :         break;
     595                 :             : 
     596                 :             :       /* sleep 10ms, then try again */
     597                 :           0 :       g_usleep (timeout_usec);
     598                 :             : 
     599                 :             :       /* If the mtime of the lock file changed, don’t count the retry, as it
     600                 :             :        * seems like there’s contention between processes for the lock file,
     601                 :             :        * rather than a stale lock file from a crashed process. */
     602                 :           0 :       if (num_tries > 0 && lock_mtime_nsec != lock_mtime_nsec_prev)
     603                 :           0 :         num_tries--;
     604                 :             :     }
     605                 :             : 
     606                 :          68 :   if (num_tries == max_tries)
     607                 :             :     {
     608                 :             :       /* ok, we slept 50*10ms = 0.5 seconds. Conclude that the lock file must be
     609                 :             :        * stale (nuke it from orbit)
     610                 :             :        */
     611                 :           0 :       if (g_unlink (lock) != 0)
     612                 :             :         {
     613                 :           0 :           errsv = errno;
     614                 :           0 :           g_set_error (error,
     615                 :             :                        G_IO_ERROR,
     616                 :           0 :                        g_io_error_from_errno (errsv),
     617                 :             :                        _("Error deleting stale lock file “%s”: %s"),
     618                 :             :                        lock,
     619                 :             :                        g_strerror (errsv));
     620                 :           0 :           goto out;
     621                 :             :         }
     622                 :             : 
     623                 :           0 :       _log ("Deleted stale lock file '%s'", lock);
     624                 :             : 
     625                 :             :       /* Try one last time to create it, now that we've deleted the stale one */
     626                 :           0 :       ret = create_lock_exclusive (lock, NULL, error);
     627                 :           0 :       if (ret < 0)
     628                 :           0 :         goto out;
     629                 :             :     }
     630                 :             : 
     631                 :          68 : out:
     632                 :          68 :   g_free (lock);
     633                 :          68 :   return ret;
     634                 :             : }
     635                 :             : 
     636                 :             : static gboolean
     637                 :          68 : keyring_release_lock (const gchar  *path,
     638                 :             :                       gint          lock_fd,
     639                 :             :                       GError      **error)
     640                 :             : {
     641                 :             :   gchar *lock;
     642                 :             :   gboolean ret;
     643                 :             : 
     644                 :          68 :   g_return_val_if_fail (path != NULL, FALSE);
     645                 :          68 :   g_return_val_if_fail (lock_fd != -1, FALSE);
     646                 :          68 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
     647                 :             : 
     648                 :          68 :   ret = FALSE;
     649                 :          68 :   lock = g_strdup_printf ("%s.lock", path);
     650                 :          68 :   if (close (lock_fd) != 0)
     651                 :             :     {
     652                 :           0 :       int errsv = errno;
     653                 :           0 :       g_set_error (error,
     654                 :             :                    G_IO_ERROR,
     655                 :           0 :                    g_io_error_from_errno (errsv),
     656                 :             :                    _("Error closing (unlinked) lock file “%s”: %s"),
     657                 :             :                    lock,
     658                 :             :                    g_strerror (errsv));
     659                 :           0 :       goto out;
     660                 :             :     }
     661                 :          68 :   if (g_unlink (lock) != 0)
     662                 :             :     {
     663                 :           0 :       int errsv = errno;
     664                 :           0 :       g_set_error (error,
     665                 :             :                    G_IO_ERROR,
     666                 :           0 :                    g_io_error_from_errno (errsv),
     667                 :             :                    _("Error unlinking lock file “%s”: %s"),
     668                 :             :                    lock,
     669                 :             :                    g_strerror (errsv));
     670                 :           0 :       goto out;
     671                 :             :     }
     672                 :             : 
     673                 :          68 :   ret = TRUE;
     674                 :             : 
     675                 :          68 :  out:
     676                 :          68 :   g_free (lock);
     677                 :          68 :   return ret;
     678                 :             : }
     679                 :             : 
     680                 :             : 
     681                 :             : /* adds an entry to the keyring, taking care of locking and deleting stale/future entries */
     682                 :             : static gboolean
     683                 :          68 : keyring_generate_entry (const gchar  *cookie_context,
     684                 :             :                         gint         *out_id,
     685                 :             :                         gchar       **out_cookie,
     686                 :             :                         GError      **error)
     687                 :             : {
     688                 :             :   gboolean ret;
     689                 :             :   gchar *keyring_dir;
     690                 :             :   gchar *path;
     691                 :             :   gchar *contents;
     692                 :          68 :   GError *local_error = NULL;
     693                 :             :   gchar **lines;
     694                 :             :   gint max_line_id;
     695                 :             :   GString *new_contents;
     696                 :             :   gint64 now;
     697                 :             :   gboolean have_id;
     698                 :             :   gint use_id;
     699                 :             :   gchar *use_cookie;
     700                 :             :   gboolean changed_file;
     701                 :             :   gint lock_fd;
     702                 :             : 
     703                 :          68 :   g_return_val_if_fail (cookie_context != NULL, FALSE);
     704                 :          68 :   g_return_val_if_fail (out_id != NULL, FALSE);
     705                 :          68 :   g_return_val_if_fail (out_cookie != NULL, FALSE);
     706                 :          68 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
     707                 :             : 
     708                 :          68 :   ret = FALSE;
     709                 :          68 :   path = NULL;
     710                 :          68 :   contents = NULL;
     711                 :          68 :   lines = NULL;
     712                 :          68 :   new_contents = NULL;
     713                 :          68 :   have_id = FALSE;
     714                 :          68 :   use_id = 0;
     715                 :          68 :   use_cookie = NULL;
     716                 :          68 :   lock_fd = -1;
     717                 :             : 
     718                 :          68 :   keyring_dir = ensure_keyring_directory (error);
     719                 :          68 :   if (keyring_dir == NULL)
     720                 :           0 :     goto out;
     721                 :             : 
     722                 :          68 :   path = g_build_filename (keyring_dir, cookie_context, NULL);
     723                 :             : 
     724                 :          68 :   lock_fd = keyring_acquire_lock (path, error);
     725                 :          68 :   if (lock_fd == -1)
     726                 :           0 :     goto out;
     727                 :             : 
     728                 :          68 :   contents = NULL;
     729                 :          68 :   if (!g_file_get_contents (path,
     730                 :             :                             &contents,
     731                 :             :                             NULL,
     732                 :             :                             &local_error))
     733                 :             :     {
     734                 :           7 :       if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
     735                 :             :         {
     736                 :             :           /* file doesn't have to exist */
     737                 :           7 :           g_clear_error (&local_error);
     738                 :             :         }
     739                 :             :       else
     740                 :             :         {
     741                 :           0 :           g_propagate_prefixed_error (error,
     742                 :           0 :                                       g_steal_pointer (&local_error),
     743                 :             :                                       _("Error opening keyring “%s” for writing: "),
     744                 :             :                                       path);
     745                 :           0 :           goto out;
     746                 :             :         }
     747                 :             :     }
     748                 :             : 
     749                 :          68 :   new_contents = g_string_new (NULL);
     750                 :          68 :   now = g_get_real_time () / G_USEC_PER_SEC;
     751                 :          68 :   changed_file = FALSE;
     752                 :             : 
     753                 :          68 :   max_line_id = 0;
     754                 :          68 :   if (contents != NULL)
     755                 :             :     {
     756                 :             :       guint n;
     757                 :          61 :       lines = g_strsplit (contents, "\n", 0);
     758                 :         183 :       for (n = 0; lines[n] != NULL; n++)
     759                 :             :         {
     760                 :         122 :           const gchar *line = lines[n];
     761                 :             :           gchar **tokens;
     762                 :             :           gchar *endp;
     763                 :             :           gint line_id;
     764                 :             :           gint64 line_when;
     765                 :             :           gboolean keep_entry;
     766                 :             : 
     767                 :         122 :           if (line[0] == '\0')
     768                 :          61 :             continue;
     769                 :             : 
     770                 :          61 :           tokens = g_strsplit (line, " ", 0);
     771                 :          61 :           if (g_strv_length (tokens) != 3)
     772                 :             :             {
     773                 :           0 :               g_set_error (error,
     774                 :             :                            G_IO_ERROR,
     775                 :             :                            G_IO_ERROR_FAILED,
     776                 :             :                            _("Line %d of the keyring at “%s” with content “%s” is malformed"),
     777                 :             :                            n + 1,
     778                 :             :                            path,
     779                 :             :                            line);
     780                 :           0 :               g_strfreev (tokens);
     781                 :           0 :               goto out;
     782                 :             :             }
     783                 :             : 
     784                 :          61 :           line_id = g_ascii_strtoll (tokens[0], &endp, 10);
     785                 :          61 :           if (*endp != '\0')
     786                 :             :             {
     787                 :           0 :               g_set_error (error,
     788                 :             :                            G_IO_ERROR,
     789                 :             :                            G_IO_ERROR_FAILED,
     790                 :             :                            _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
     791                 :             :                            n + 1,
     792                 :             :                            path,
     793                 :             :                            line);
     794                 :           0 :               g_strfreev (tokens);
     795                 :           0 :               goto out;
     796                 :             :             }
     797                 :             : 
     798                 :          61 :           line_when = g_ascii_strtoll (tokens[1], &endp, 10);
     799                 :          61 :           if (*endp != '\0')
     800                 :             :             {
     801                 :           0 :               g_set_error (error,
     802                 :             :                            G_IO_ERROR,
     803                 :             :                            G_IO_ERROR_FAILED,
     804                 :             :                            _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
     805                 :             :                            n + 1,
     806                 :             :                            path,
     807                 :             :                            line);
     808                 :           0 :               g_strfreev (tokens);
     809                 :           0 :               goto out;
     810                 :             :             }
     811                 :             : 
     812                 :             : 
     813                 :             :           /* D-Bus spec says:
     814                 :             :            *
     815                 :             :            *  Once the lockfile has been created, the server loads the
     816                 :             :            *  cookie file. It should then delete any cookies that are
     817                 :             :            *  old (the timeout can be fairly short), or more than a
     818                 :             :            *  reasonable time in the future (so that cookies never
     819                 :             :            *  accidentally become permanent, if the clock was set far
     820                 :             :            *  into the future at some point). If no recent keys remain,
     821                 :             :            *  the server may generate a new key.
     822                 :             :            *
     823                 :             :            */
     824                 :          61 :           keep_entry = TRUE;
     825                 :          61 :           if (line_when > now)
     826                 :             :             {
     827                 :             :               /* Oddball case: entry is more recent than our current wall-clock time..
     828                 :             :                * This is OK, it means that another server on another machine but with
     829                 :             :                * same $HOME wrote the entry. */
     830                 :           0 :               if (line_when - now > MAX_TIME_TRAVEL_SECONDS)
     831                 :             :                 {
     832                 :           0 :                   keep_entry = FALSE;
     833                 :           0 :                   _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
     834                 :             :                 }
     835                 :             :             }
     836                 :             :           else
     837                 :             :             {
     838                 :             :               /* Discard entry if it's too old. */
     839                 :          61 :               if (now - line_when > EXPIRE_KEYS_TIMEOUT_SECONDS)
     840                 :             :                 {
     841                 :           0 :                   keep_entry = FALSE;
     842                 :             :                 }
     843                 :             :             }
     844                 :             : 
     845                 :          61 :           if (!keep_entry)
     846                 :             :             {
     847                 :           0 :               changed_file = FALSE;
     848                 :             :             }
     849                 :             :           else
     850                 :             :             {
     851                 :          61 :               g_string_append_printf (new_contents,
     852                 :             :                                       "%d %" G_GUINT64_FORMAT " %s\n",
     853                 :             :                                       line_id,
     854                 :             :                                       line_when,
     855                 :          61 :                                       tokens[2]);
     856                 :          61 :               max_line_id = MAX (line_id, max_line_id);
     857                 :             :               /* Only reuse entry if not older than 5 minutes.
     858                 :             :                *
     859                 :             :                * (We need a bit of grace time compared to 7 minutes above.. otherwise
     860                 :             :                * there's a race where we reuse the 6min59.9 secs old entry and a
     861                 :             :                * split-second later another server purges the now 7 minute old entry.)
     862                 :             :                */
     863                 :          61 :               if (now - line_when < NEW_KEY_TIMEOUT_SECONDS)
     864                 :             :                 {
     865                 :          61 :                   if (!have_id)
     866                 :             :                     {
     867                 :          61 :                       use_id = line_id;
     868                 :          61 :                       use_cookie = tokens[2]; /* steal memory */
     869                 :          61 :                       tokens[2] = NULL;
     870                 :          61 :                       have_id = TRUE;
     871                 :             :                     }
     872                 :             :                 }
     873                 :             :             }
     874                 :          61 :           g_strfreev (tokens);
     875                 :             :         }
     876                 :             :     } /* for each line */
     877                 :             : 
     878                 :          68 :   ret = TRUE;
     879                 :             : 
     880                 :          68 :   if (have_id)
     881                 :             :     {
     882                 :          61 :       *out_id = use_id;
     883                 :          61 :       *out_cookie = use_cookie;
     884                 :          61 :       use_cookie = NULL;
     885                 :             :     }
     886                 :             :   else
     887                 :             :     {
     888                 :             :       gchar *raw_cookie;
     889                 :           7 :       *out_id = max_line_id + 1;
     890                 :           7 :       raw_cookie = random_blob (32);
     891                 :           7 :       *out_cookie = _g_dbus_hexencode (raw_cookie, 32);
     892                 :           7 :       g_free (raw_cookie);
     893                 :             : 
     894                 :           7 :       g_string_append_printf (new_contents,
     895                 :             :                               "%d %" G_GINT64_FORMAT " %s\n",
     896                 :             :                               *out_id,
     897                 :           7 :                               g_get_real_time () / G_USEC_PER_SEC,
     898                 :             :                               *out_cookie);
     899                 :           7 :       changed_file = TRUE;
     900                 :             :     }
     901                 :             : 
     902                 :             :   /* and now actually write the cookie file if there are changes (this is atomic) */
     903                 :          68 :   if (changed_file)
     904                 :             :     {
     905                 :           7 :       if (!g_file_set_contents_full (path,
     906                 :           7 :                                      new_contents->str,
     907                 :             :                                      -1,
     908                 :             :                                      G_FILE_SET_CONTENTS_CONSISTENT,
     909                 :             :                                      0600,
     910                 :             :                                      error))
     911                 :             :         {
     912                 :           0 :           *out_id = 0;
     913                 :           0 :           g_free (*out_cookie);
     914                 :           0 :           *out_cookie = 0;
     915                 :           0 :           ret = FALSE;
     916                 :           0 :           goto out;
     917                 :             :         }
     918                 :             :     }
     919                 :             : 
     920                 :          68 :  out:
     921                 :             :   /* Any error should have been propagated to @error by now */
     922                 :          68 :   g_assert (local_error == NULL);
     923                 :             : 
     924                 :          68 :   if (lock_fd != -1)
     925                 :             :     {
     926                 :          68 :       if (!keyring_release_lock (path, lock_fd, &local_error))
     927                 :             :         {
     928                 :           0 :           if (error != NULL)
     929                 :             :             {
     930                 :           0 :               if (*error == NULL)
     931                 :             :                 {
     932                 :           0 :                   *error = local_error;
     933                 :             :                 }
     934                 :             :               else
     935                 :             :                 {
     936                 :           0 :                   g_prefix_error (error,
     937                 :             :                                   _("(Additionally, releasing the lock for “%s” also failed: %s) "),
     938                 :             :                                   path,
     939                 :           0 :                                   local_error->message);
     940                 :           0 :                   g_error_free (local_error);
     941                 :             :                 }
     942                 :             :             }
     943                 :             :           else
     944                 :             :             {
     945                 :           0 :               g_error_free (local_error);
     946                 :             :             }
     947                 :             :         }
     948                 :             :     }
     949                 :             : 
     950                 :          68 :   g_free (keyring_dir);
     951                 :          68 :   g_free (path);
     952                 :          68 :   g_strfreev (lines);
     953                 :          68 :   g_free (contents);
     954                 :          68 :   if (new_contents != NULL)
     955                 :          68 :     g_string_free (new_contents, TRUE);
     956                 :          68 :   g_free (use_cookie);
     957                 :          68 :   return ret;
     958                 :             : }
     959                 :             : 
     960                 :             : /* ---------------------------------------------------------------------------------------------------- */
     961                 :             : 
     962                 :             : static gchar *
     963                 :          77 : generate_sha1 (const gchar *server_challenge,
     964                 :             :                const gchar *client_challenge,
     965                 :             :                const gchar *cookie)
     966                 :             : {
     967                 :             :   GString *str;
     968                 :             :   gchar *sha1;
     969                 :             : 
     970                 :          77 :   str = g_string_new (server_challenge);
     971                 :             :   g_string_append_c (str, ':');
     972                 :             :   g_string_append (str, client_challenge);
     973                 :             :   g_string_append_c (str, ':');
     974                 :             :   g_string_append (str, cookie);
     975                 :          77 :   sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1);
     976                 :          77 :   g_string_free (str, TRUE);
     977                 :             : 
     978                 :          77 :   return sha1;
     979                 :             : }
     980                 :             : 
     981                 :             : /* ---------------------------------------------------------------------------------------------------- */
     982                 :             : 
     983                 :             : static GDBusAuthMechanismState
     984                 :         204 : mechanism_server_get_state (GDBusAuthMechanism   *mechanism)
     985                 :             : {
     986                 :         204 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
     987                 :             : 
     988                 :         204 :   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
     989                 :         204 :   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
     990                 :             : 
     991                 :         204 :   return m->priv->state;
     992                 :             : }
     993                 :             : 
     994                 :             : static void
     995                 :          68 : mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
     996                 :             :                            const gchar          *initial_response,
     997                 :             :                            gsize                 initial_response_len)
     998                 :             : {
     999                 :          68 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1000                 :             : 
    1001                 :          68 :   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
    1002                 :          68 :   g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
    1003                 :             : 
    1004                 :          68 :   m->priv->is_server = TRUE;
    1005                 :          68 :   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1006                 :             : 
    1007                 :          68 :   if (initial_response != NULL && initial_response_len > 0)
    1008                 :             :     {
    1009                 :             : #ifdef G_OS_UNIX
    1010                 :             :       gint64 uid;
    1011                 :             :       gchar *endp;
    1012                 :             : 
    1013                 :          68 :       uid = g_ascii_strtoll (initial_response, &endp, 10);
    1014                 :          68 :       if (*endp == '\0')
    1015                 :             :         {
    1016                 :          68 :           if (uid == getuid ())
    1017                 :             :             {
    1018                 :          68 :               m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
    1019                 :             :             }
    1020                 :             :         }
    1021                 :             : #elif defined(G_OS_WIN32)
    1022                 :             :       gchar *sid;
    1023                 :             : 
    1024                 :             :       sid = _g_win32_current_process_sid_string (NULL);
    1025                 :             : 
    1026                 :             :       if (g_strcmp0 (initial_response, sid) == 0)
    1027                 :             :         m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
    1028                 :             : 
    1029                 :             :       g_free (sid);
    1030                 :             : #else
    1031                 :             : #error Please implement for your OS
    1032                 :             : #endif
    1033                 :             :     }
    1034                 :             : }
    1035                 :             : 
    1036                 :             : static void
    1037                 :          68 : mechanism_server_data_receive (GDBusAuthMechanism   *mechanism,
    1038                 :             :                                const gchar          *data,
    1039                 :             :                                gsize                 data_len)
    1040                 :             : {
    1041                 :          68 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1042                 :             :   gchar **tokens;
    1043                 :             :   const gchar *client_challenge;
    1044                 :             :   const gchar *alleged_sha1;
    1045                 :             :   gchar *sha1;
    1046                 :             : 
    1047                 :          68 :   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
    1048                 :          68 :   g_return_if_fail (m->priv->is_server && !m->priv->is_client);
    1049                 :          68 :   g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
    1050                 :             : 
    1051                 :          68 :   tokens = NULL;
    1052                 :          68 :   sha1 = NULL;
    1053                 :             : 
    1054                 :          68 :   tokens = g_strsplit (data, " ", 0);
    1055                 :          68 :   if (g_strv_length (tokens) != 2)
    1056                 :             :     {
    1057                 :           0 :       g_free (m->priv->reject_reason);
    1058                 :           0 :       m->priv->reject_reason = g_strdup_printf ("Malformed data '%s'", data);
    1059                 :           0 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1060                 :           0 :       goto out;
    1061                 :             :     }
    1062                 :             : 
    1063                 :          68 :   client_challenge = tokens[0];
    1064                 :          68 :   alleged_sha1 = tokens[1];
    1065                 :             : 
    1066                 :          68 :   sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
    1067                 :             : 
    1068                 :          68 :   if (g_strcmp0 (sha1, alleged_sha1) == 0)
    1069                 :             :     {
    1070                 :          68 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
    1071                 :             :     }
    1072                 :             :   else
    1073                 :             :     {
    1074                 :           0 :       g_free (m->priv->reject_reason);
    1075                 :           0 :       m->priv->reject_reason = g_strdup_printf ("SHA-1 mismatch");
    1076                 :           0 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1077                 :             :     }
    1078                 :             : 
    1079                 :          68 :  out:
    1080                 :          68 :   g_strfreev (tokens);
    1081                 :          68 :   g_free (sha1);
    1082                 :             : }
    1083                 :             : 
    1084                 :             : static gchar *
    1085                 :          68 : mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
    1086                 :             :                             gsize                *out_data_len)
    1087                 :             : {
    1088                 :          68 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1089                 :             :   gchar *s;
    1090                 :             :   gint cookie_id;
    1091                 :             :   const gchar *cookie_context;
    1092                 :             :   GError *error;
    1093                 :             : 
    1094                 :          68 :   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
    1095                 :          68 :   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
    1096                 :          68 :   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
    1097                 :             : 
    1098                 :          68 :   s = NULL;
    1099                 :          68 :   *out_data_len = 0;
    1100                 :             : 
    1101                 :             :   /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
    1102                 :          68 :   cookie_context = "org_gtk_gdbus_general";
    1103                 :             : 
    1104                 :          68 :   cookie_id = -1;
    1105                 :          68 :   error = NULL;
    1106                 :          68 :   if (!keyring_generate_entry (cookie_context,
    1107                 :             :                                &cookie_id,
    1108                 :          68 :                                &m->priv->cookie,
    1109                 :             :                                &error))
    1110                 :             :     {
    1111                 :           0 :       g_free (m->priv->reject_reason);
    1112                 :           0 :       m->priv->reject_reason = g_strdup_printf ("Error adding entry to keyring: %s", error->message);
    1113                 :           0 :       g_error_free (error);
    1114                 :           0 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1115                 :           0 :       goto out;
    1116                 :             :     }
    1117                 :             : 
    1118                 :          68 :   m->priv->server_challenge = random_ascii_string (16);
    1119                 :          68 :   s = g_strdup_printf ("%s %d %s",
    1120                 :             :                        cookie_context,
    1121                 :             :                        cookie_id,
    1122                 :          68 :                        m->priv->server_challenge);
    1123                 :          68 :   *out_data_len = strlen (s);
    1124                 :             : 
    1125                 :          68 :   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
    1126                 :             : 
    1127                 :          68 :  out:
    1128                 :          68 :   return s;
    1129                 :             : }
    1130                 :             : 
    1131                 :             : static gchar *
    1132                 :          15 : mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism)
    1133                 :             : {
    1134                 :          15 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1135                 :             : 
    1136                 :          15 :   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
    1137                 :          15 :   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
    1138                 :             : 
    1139                 :          30 :   return g_strdup (m->priv->reject_reason);
    1140                 :             : }
    1141                 :             : 
    1142                 :             : static void
    1143                 :           0 : mechanism_server_shutdown (GDBusAuthMechanism   *mechanism)
    1144                 :             : {
    1145                 :           0 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1146                 :             : 
    1147                 :           0 :   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
    1148                 :           0 :   g_return_if_fail (m->priv->is_server && !m->priv->is_client);
    1149                 :             : 
    1150                 :           0 :   m->priv->is_server = FALSE;
    1151                 :             : }
    1152                 :             : 
    1153                 :             : /* ---------------------------------------------------------------------------------------------------- */
    1154                 :             : 
    1155                 :             : static GDBusAuthMechanismState
    1156                 :          33 : mechanism_client_get_state (GDBusAuthMechanism   *mechanism)
    1157                 :             : {
    1158                 :          33 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1159                 :             : 
    1160                 :          33 :   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
    1161                 :          33 :   g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
    1162                 :             : 
    1163                 :          33 :   return m->priv->state;
    1164                 :             : }
    1165                 :             : 
    1166                 :             : static gchar *
    1167                 :          24 : mechanism_client_initiate (GDBusAuthMechanism   *mechanism,
    1168                 :             :                            GDBusConnectionFlags  conn_flags,
    1169                 :             :                            gsize                *out_initial_response_len)
    1170                 :             : {
    1171                 :          24 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1172                 :             :   gchar *initial_response;
    1173                 :             : 
    1174                 :          24 :   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
    1175                 :          24 :   g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
    1176                 :             : 
    1177                 :          24 :   m->priv->is_client = TRUE;
    1178                 :             : 
    1179                 :          24 :   *out_initial_response_len = 0;
    1180                 :             : 
    1181                 :             : #ifdef G_OS_UNIX
    1182                 :          24 :   initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
    1183                 :             : #elif defined (G_OS_WIN32)
    1184                 :             :   initial_response = _g_win32_current_process_sid_string (NULL);
    1185                 :             : #else
    1186                 :             : #error Please implement for your OS
    1187                 :             : #endif
    1188                 :          24 :   if (initial_response)
    1189                 :             :     {
    1190                 :          24 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
    1191                 :          24 :       *out_initial_response_len = strlen (initial_response);
    1192                 :             :     }
    1193                 :             :   else
    1194                 :             :     {
    1195                 :           0 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1196                 :             :     }
    1197                 :             : 
    1198                 :          24 :   return initial_response;
    1199                 :             : }
    1200                 :             : 
    1201                 :             : /* Context names must be valid ASCII, nonzero length, and may not contain the
    1202                 :             :  * characters slash ("/"), backslash ("\"), space (" "), newline ("\n"),
    1203                 :             :  * carriage return ("\r"), tab ("\t"), or period (".").
    1204                 :             :  *
    1205                 :             :  * See https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha */
    1206                 :             : static gboolean
    1207                 :          22 : validate_cookie_context (const char *cookie_context)
    1208                 :             : {
    1209                 :          22 :   size_t i = 0;
    1210                 :             : 
    1211                 :          22 :   g_return_val_if_fail (cookie_context != NULL, FALSE);
    1212                 :             : 
    1213                 :         318 :   for (i = 0; cookie_context[i] != '\0'; i++)
    1214                 :             :     {
    1215                 :         303 :       if ((uint8_t) cookie_context[i] >= 128 ||
    1216                 :         302 :           cookie_context[i] == '/' ||
    1217                 :         301 :           cookie_context[i] == '\\' ||
    1218                 :         300 :           cookie_context[i] == ' ' ||
    1219                 :         300 :           cookie_context[i] == '\n' ||
    1220                 :         299 :           cookie_context[i] == '\r' ||
    1221                 :         298 :           cookie_context[i] == '\t' ||
    1222                 :         297 :           cookie_context[i] == '.')
    1223                 :           7 :         return FALSE;
    1224                 :             :     }
    1225                 :             : 
    1226                 :          15 :   return (i > 0);
    1227                 :             : }
    1228                 :             : 
    1229                 :             : static void
    1230                 :          24 : mechanism_client_data_receive (GDBusAuthMechanism   *mechanism,
    1231                 :             :                                const gchar          *data,
    1232                 :             :                                gsize                 data_len)
    1233                 :             : {
    1234                 :          24 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1235                 :             :   gchar **tokens;
    1236                 :             :   const gchar *cookie_context;
    1237                 :             :   int64_t cookie_id;
    1238                 :             :   const gchar *server_challenge;
    1239                 :             :   gchar *client_challenge;
    1240                 :             :   gchar *endp;
    1241                 :             :   gchar *cookie;
    1242                 :             :   GError *error;
    1243                 :             :   gchar *sha1;
    1244                 :             : 
    1245                 :          24 :   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
    1246                 :          24 :   g_return_if_fail (m->priv->is_client && !m->priv->is_server);
    1247                 :          24 :   g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
    1248                 :             : 
    1249                 :          24 :   tokens = NULL;
    1250                 :          24 :   cookie = NULL;
    1251                 :          24 :   client_challenge = NULL;
    1252                 :             : 
    1253                 :          24 :   tokens = g_strsplit (data, " ", 0);
    1254                 :          24 :   if (g_strv_length (tokens) != 3)
    1255                 :             :     {
    1256                 :           2 :       g_free (m->priv->reject_reason);
    1257                 :           2 :       m->priv->reject_reason = g_strdup_printf ("Malformed data '%s'", data);
    1258                 :           2 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1259                 :           2 :       goto out;
    1260                 :             :     }
    1261                 :             : 
    1262                 :          22 :   cookie_context = tokens[0];
    1263                 :          22 :   if (!validate_cookie_context (tokens[0]))
    1264                 :             :     {
    1265                 :           8 :       g_free (m->priv->reject_reason);
    1266                 :           8 :       m->priv->reject_reason = g_strdup_printf ("Malformed cookie_context '%s'", tokens[0]);
    1267                 :           8 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1268                 :           8 :       goto out;
    1269                 :             :     }
    1270                 :             : 
    1271                 :          14 :   cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
    1272                 :          14 :   if (*endp != '\0' || endp == tokens[1] || cookie_id < 0 || cookie_id > UINT32_MAX)
    1273                 :             :     {
    1274                 :           4 :       g_free (m->priv->reject_reason);
    1275                 :           4 :       m->priv->reject_reason = g_strdup_printf ("Malformed cookie_id '%s'", tokens[1]);
    1276                 :           4 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1277                 :           4 :       goto out;
    1278                 :             :     }
    1279                 :          10 :   server_challenge = tokens[2];
    1280                 :             : 
    1281                 :          10 :   error = NULL;
    1282                 :          10 :   cookie = keyring_lookup_entry (cookie_context, (unsigned int) cookie_id, &error);
    1283                 :          10 :   if (cookie == NULL)
    1284                 :             :     {
    1285                 :           1 :       g_free (m->priv->reject_reason);
    1286                 :           1 :       m->priv->reject_reason = g_strdup_printf ("Problems looking up entry in keyring: %s", error->message);
    1287                 :           1 :       g_error_free (error);
    1288                 :           1 :       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
    1289                 :           1 :       goto out;
    1290                 :             :     }
    1291                 :             : 
    1292                 :           9 :   client_challenge = random_ascii_string (16);
    1293                 :           9 :   sha1 = generate_sha1 (server_challenge, client_challenge, cookie);
    1294                 :           9 :   m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1);
    1295                 :           9 :   g_free (sha1);
    1296                 :           9 :   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
    1297                 :             : 
    1298                 :          24 :  out:
    1299                 :          24 :   g_strfreev (tokens);
    1300                 :          24 :   g_free (cookie);
    1301                 :          24 :   g_free (client_challenge);
    1302                 :             : }
    1303                 :             : 
    1304                 :             : static gchar *
    1305                 :           9 : mechanism_client_data_send (GDBusAuthMechanism   *mechanism,
    1306                 :             :                             gsize                *out_data_len)
    1307                 :             : {
    1308                 :           9 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1309                 :             : 
    1310                 :           9 :   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
    1311                 :           9 :   g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
    1312                 :           9 :   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
    1313                 :             : 
    1314                 :           9 :   g_assert (m->priv->to_send != NULL);
    1315                 :             : 
    1316                 :           9 :   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
    1317                 :             : 
    1318                 :           9 :   *out_data_len = strlen (m->priv->to_send);
    1319                 :          18 :   return g_strdup (m->priv->to_send);
    1320                 :             : }
    1321                 :             : 
    1322                 :             : static void
    1323                 :          15 : mechanism_client_shutdown (GDBusAuthMechanism   *mechanism)
    1324                 :             : {
    1325                 :          15 :   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
    1326                 :             : 
    1327                 :          15 :   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
    1328                 :          15 :   g_return_if_fail (m->priv->is_client && !m->priv->is_server);
    1329                 :             : 
    1330                 :          15 :   m->priv->is_client = FALSE;
    1331                 :             : }
    1332                 :             : 
    1333                 :             : /* ---------------------------------------------------------------------------------------------------- */
        

Generated by: LCOV version 2.0-1