LCOV - code coverage report
Current view: top level - glib/gio/tests - gdbus-sasl.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 126 127 99.2 %
Date: 2024-04-23 05:16:05 Functions: 5 5 100.0 %
Branches: 14 18 77.8 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright 2019-2022 Collabora Ltd.
       3                 :            :  *
       4                 :            :  * SPDX-License-Identifier: LGPL-2.1-or-later
       5                 :            :  *
       6                 :            :  * This library is free software; you can redistribute it and/or
       7                 :            :  * modify it under the terms of the GNU Lesser General Public
       8                 :            :  * License as published by the Free Software Foundation; either
       9                 :            :  * version 2.1 of the License, or (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This library is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14                 :            :  * Lesser General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU Lesser General
      17                 :            :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18                 :            :  */
      19                 :            : 
      20                 :            : #include "config.h"
      21                 :            : 
      22                 :            : #include <errno.h>
      23                 :            : #include <locale.h>
      24                 :            : #include <stdlib.h>
      25                 :            : #include <string.h>
      26                 :            : 
      27                 :            : #include <glib.h>
      28                 :            : #include <glib/gstdio.h>
      29                 :            : #include <gio/gio.h>
      30                 :            : 
      31                 :            : /* For G_CREDENTIALS_*_SUPPORTED */
      32                 :            : #include <gio/gcredentialsprivate.h>
      33                 :            : 
      34                 :            : static const char * const explicit_external_initial_response_fail[] =
      35                 :            : {
      36                 :            :   "EXTERNAL with incorrect initial response",
      37                 :            :   "C:AUTH EXTERNAL <wrong-uid>",
      38                 :            :   "S:REJECTED.*$",
      39                 :            :   NULL
      40                 :            : };
      41                 :            : 
      42                 :            : static const char * const explicit_external_fail[] =
      43                 :            : {
      44                 :            :   "EXTERNAL without initial response, failing to authenticate",
      45                 :            :   "C:AUTH EXTERNAL",
      46                 :            :   "S:DATA$",
      47                 :            :   "C:DATA <wrong-uid>",
      48                 :            :   "S:REJECTED.*$",
      49                 :            :   NULL
      50                 :            : };
      51                 :            : 
      52                 :            : #if defined(G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED) || defined(G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED)
      53                 :            : static const char * const explicit_external_initial_response[] =
      54                 :            : {
      55                 :            :   "EXTERNAL with initial response",
      56                 :            :   /* This is what most older D-Bus libraries do. */
      57                 :            :   "C:AUTH EXTERNAL <uid>",          /* I claim to be <uid> */
      58                 :            :   "S:OK [0-9a-f]+$",
      59                 :            :   NULL
      60                 :            : };
      61                 :            : 
      62                 :            : static const char * const explicit_external[] =
      63                 :            : {
      64                 :            :   "EXTERNAL without initial response",
      65                 :            :   /* In theory this is equally valid, although many D-Bus libraries
      66                 :            :    * probably don't support it correctly. */
      67                 :            :   "C:AUTH EXTERNAL",                /* Start EXTERNAL, no initial response */
      68                 :            :   "S:DATA$",                        /* Who are you? */
      69                 :            :   "C:DATA <uid>",                   /* I claim to be <uid> */
      70                 :            :   "S:OK [0-9a-f]+$",
      71                 :            :   NULL
      72                 :            : };
      73                 :            : 
      74                 :            : static const char * const implicit_external[] =
      75                 :            : {
      76                 :            :   "EXTERNAL with empty authorization identity",
      77                 :            :   /* This is what sd-bus does. */
      78                 :            :   "C:AUTH EXTERNAL",                /* Start EXTERNAL, no initial response */
      79                 :            :   "S:DATA$",                        /* Who are you? */
      80                 :            :   "C:DATA",                         /* I'm whoever the kernel says I am */
      81                 :            :   "S:OK [0-9a-f]+$",
      82                 :            :   NULL
      83                 :            : };
      84                 :            : 
      85                 :            : static const char * const implicit_external_space[] =
      86                 :            : {
      87                 :            :   "EXTERNAL with empty authorization identity and whitespace",
      88                 :            :   /* GDBus used to represent empty data blocks like this, although it
      89                 :            :    * isn't interoperable to do so (in particular sd-bus would reject this). */
      90                 :            :   "C:AUTH EXTERNAL",                /* Start EXTERNAL, no initial response */
      91                 :            :   "S:DATA$",                        /* Who are you? */
      92                 :            :   "C:DATA ",                        /* I'm whoever the kernel says I am */
      93                 :            :   "S:OK [0-9a-f]+$",
      94                 :            :   NULL
      95                 :            : };
      96                 :            : #endif
      97                 :            : 
      98                 :            : static const char * const * const handshakes[] =
      99                 :            : {
     100                 :            :   explicit_external_initial_response_fail,
     101                 :            :   explicit_external_fail,
     102                 :            : #if defined(G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED) || defined(G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED)
     103                 :            :   explicit_external_initial_response,
     104                 :            :   explicit_external,
     105                 :            :   implicit_external,
     106                 :            :   implicit_external_space,
     107                 :            : #endif
     108                 :            : };
     109                 :            : 
     110                 :            : static void
     111                 :          2 : encode_uid (guint uid,
     112                 :            :             GString *dest)
     113                 :            : {
     114                 :          2 :   gchar *str = g_strdup_printf ("%u", uid);
     115                 :            :   gchar *p;
     116                 :            : 
     117                 :          2 :   g_string_assign (dest, "");
     118                 :            : 
     119         [ +  + ]:          7 :   for (p = str; *p != '\0'; p++)
     120                 :          5 :     g_string_append_printf (dest, "%02x", (unsigned char) *p);
     121                 :            : 
     122                 :          2 :   g_free (str);
     123                 :          2 : }
     124                 :            : 
     125                 :            : typedef struct
     126                 :            : {
     127                 :            :   GCond cond;
     128                 :            :   GMutex mutex;
     129                 :            :   GDBusServerFlags server_flags;
     130                 :            :   GMainContext *ctx;
     131                 :            :   GMainLoop *loop;
     132                 :            :   gchar *guid;
     133                 :            :   gchar *listenable_address;
     134                 :            :   gboolean ready;
     135                 :            : } ServerInfo;
     136                 :            : 
     137                 :            : static gboolean
     138                 :          1 : idle_in_server_thread_cb (gpointer user_data)
     139                 :            : {
     140                 :          1 :   ServerInfo *info = user_data;
     141                 :            : 
     142                 :          1 :   g_mutex_lock (&info->mutex);
     143                 :          1 :   info->ready = TRUE;
     144                 :          1 :   g_cond_broadcast (&info->cond);
     145                 :          1 :   g_mutex_unlock (&info->mutex);
     146                 :          1 :   return G_SOURCE_REMOVE;
     147                 :            : }
     148                 :            : 
     149                 :            : static gpointer
     150                 :          1 : server_thread_cb (gpointer user_data)
     151                 :            : {
     152                 :          1 :   GDBusServer *server = NULL;
     153                 :          1 :   GError *error = NULL;
     154                 :            :   GSource *source;
     155                 :          1 :   ServerInfo *info = user_data;
     156                 :            : 
     157                 :          1 :   g_main_context_push_thread_default (info->ctx);
     158                 :          2 :   server = g_dbus_server_new_sync (info->listenable_address,
     159                 :            :                                    info->server_flags,
     160                 :          1 :                                    info->guid,
     161                 :            :                                    NULL,
     162                 :            :                                    NULL,
     163                 :            :                                    &error);
     164                 :          1 :   g_assert_no_error (error);
     165                 :          1 :   g_assert_nonnull (server);
     166                 :          1 :   g_dbus_server_start (server);
     167                 :            : 
     168                 :            :   /* Tell the main thread when the server is ready to accept connections */
     169                 :          1 :   source = g_idle_source_new ();
     170                 :          1 :   g_source_set_callback (source, idle_in_server_thread_cb, info, NULL);
     171                 :          1 :   g_source_attach (source, info->ctx);
     172                 :          1 :   g_source_unref (source);
     173                 :            : 
     174                 :          1 :   g_main_loop_run (info->loop);
     175                 :            : 
     176                 :          1 :   g_main_context_pop_thread_default (info->ctx);
     177                 :          1 :   g_dbus_server_stop (server);
     178                 :          1 :   g_clear_object (&server);
     179                 :          1 :   return NULL;
     180                 :            : }
     181                 :            : 
     182                 :            : static void
     183                 :          1 : test_sasl_server (void)
     184                 :            : {
     185                 :          1 :   GError *error = NULL;
     186                 :          1 :   GSocketAddress *addr = NULL;
     187                 :          1 :   GString *buf = g_string_new ("");
     188                 :          1 :   GString *encoded_uid = g_string_new ("");
     189                 :          1 :   GString *encoded_wrong_uid = g_string_new ("");
     190                 :          1 :   GThread *server_thread = NULL;
     191                 :          1 :   ServerInfo info =
     192                 :            :   {
     193                 :            :     .server_flags = G_DBUS_SERVER_FLAGS_RUN_IN_THREAD,
     194                 :            :   };
     195                 :          1 :   gchar *escaped = NULL;
     196                 :          1 :   gchar *path = NULL;
     197                 :          1 :   gchar *tmpdir = NULL;
     198                 :            :   gsize i;
     199                 :            : 
     200                 :          1 :   tmpdir = g_dir_make_tmp ("gdbus-server-auth-XXXXXX", &error);
     201                 :          1 :   g_assert_no_error (error);
     202                 :          1 :   escaped = g_dbus_address_escape_value (tmpdir);
     203                 :            : 
     204                 :          1 :   path = g_build_filename (tmpdir, "socket", NULL);
     205                 :          1 :   g_cond_init (&info.cond);
     206                 :          1 :   g_mutex_init (&info.mutex);
     207                 :          1 :   info.ctx = g_main_context_new ();
     208                 :          1 :   info.guid = g_dbus_generate_guid ();
     209                 :          1 :   info.listenable_address = g_strdup_printf ("unix:path=%s/socket", escaped);
     210                 :          1 :   info.loop = g_main_loop_new (info.ctx, FALSE);
     211                 :          1 :   info.ready = FALSE;
     212                 :          1 :   server_thread = g_thread_new ("GDBusServer", server_thread_cb, &info);
     213                 :            : 
     214                 :          1 :   g_mutex_lock (&info.mutex);
     215                 :            : 
     216         [ +  + ]:          2 :   while (!info.ready)
     217                 :          1 :     g_cond_wait (&info.cond, &info.mutex);
     218                 :            : 
     219                 :          1 :   g_mutex_unlock (&info.mutex);
     220                 :            : 
     221                 :          1 :   addr = g_unix_socket_address_new (path);
     222                 :            : 
     223                 :          1 :   encode_uid (geteuid (), encoded_uid);
     224         [ -  + ]:          1 :   encode_uid (geteuid () == 0 ? 65534 : 0, encoded_wrong_uid);
     225                 :            : 
     226         [ +  + ]:          7 :   for (i = 0; i < G_N_ELEMENTS (handshakes); i++)
     227                 :            :     {
     228                 :          6 :       const char * const *handshake = handshakes[i];
     229                 :            :       GSocketClient *client;
     230                 :            :       GSocketConnection *conn;
     231                 :            :       GUnixConnection *conn_unix;       /* unowned */
     232                 :            :       GInputStream *istream;            /* unowned */
     233                 :            :       GDataInputStream *istream_data;
     234                 :            :       GOutputStream *ostream;           /* unowned */
     235                 :          6 :       GError *error = NULL;
     236                 :            :       gsize j;
     237                 :            : 
     238                 :          6 :       g_test_message ("New handshake: %s", handshake[0]);
     239                 :            : 
     240                 :          6 :       client = g_socket_client_new ();
     241                 :          6 :       conn = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (addr),
     242                 :            :                                       NULL, &error);
     243                 :          6 :       g_assert_no_error (error);
     244                 :            : 
     245                 :          6 :       g_assert_true (G_IS_UNIX_CONNECTION (conn));
     246                 :          6 :       conn_unix = G_UNIX_CONNECTION (conn);
     247                 :          6 :       istream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
     248                 :          6 :       ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
     249                 :          6 :       istream_data = g_data_input_stream_new (istream);
     250                 :          6 :       g_data_input_stream_set_newline_type (istream_data, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
     251                 :            : 
     252                 :          6 :       g_unix_connection_send_credentials (conn_unix, NULL, &error);
     253                 :          6 :       g_assert_no_error (error);
     254                 :            : 
     255         [ +  + ]:         26 :       for (j = 1; handshake[j] != NULL; j++)
     256                 :            :         {
     257         [ +  + ]:         20 :           if (j % 2 == 1)
     258                 :            :             {
     259                 :            :               /* client to server */
     260                 :         10 :               const char *line = handshake[j];
     261                 :            : 
     262                 :         10 :               g_assert_cmpint (line[0], ==, 'C');
     263                 :         10 :               g_assert_cmpint (line[1], ==, ':');
     264                 :         10 :               g_string_assign (buf, line + 2);
     265                 :         10 :               g_string_replace (buf, "<uid>", encoded_uid->str, 0);
     266                 :         10 :               g_string_replace (buf, "<wrong-uid>", encoded_wrong_uid->str, 0);
     267                 :         10 :               g_test_message ("C:“%s”", buf->str);
     268         [ +  - ]:         10 :               g_string_append (buf, "\r\n");
     269                 :            : 
     270                 :         10 :               g_output_stream_write_all (ostream, buf->str, buf->len, NULL, NULL, &error);
     271                 :         10 :               g_assert_no_error (error);
     272                 :            :             }
     273                 :            :           else
     274                 :            :             {
     275                 :            :               /* server to client */
     276                 :         10 :               const char *pattern = handshake[j];
     277                 :            :               char *line;
     278                 :            :               gsize len;
     279                 :            : 
     280                 :         10 :               g_assert_cmpint (pattern[0], ==, 'S');
     281                 :         10 :               g_assert_cmpint (pattern[1], ==, ':');
     282                 :            : 
     283                 :         10 :               g_test_message ("Expect: /^%s/", pattern + 2);
     284                 :         10 :               line = g_data_input_stream_read_line (istream_data, &len, NULL, &error);
     285                 :         10 :               g_assert_no_error (error);
     286                 :         10 :               g_test_message ("S:“%s”", line);
     287                 :         10 :               g_assert_cmpuint (len, ==, strlen (line));
     288                 :            : 
     289         [ -  + ]:         10 :               if (!g_regex_match_simple (pattern + 2, line,
     290                 :            :                                          G_REGEX_ANCHORED,
     291                 :            :                                          G_REGEX_MATCH_ANCHORED))
     292                 :          0 :                 g_error ("Expected /^%s/, got “%s”", pattern + 2, line);
     293                 :            : 
     294                 :         10 :               g_free (line);
     295                 :            :             }
     296                 :            :         }
     297                 :            : 
     298                 :          6 :       g_object_unref (istream_data);
     299                 :          6 :       g_object_unref (conn);
     300                 :          6 :       g_object_unref (client);
     301                 :            :     }
     302                 :            : 
     303                 :          1 :   g_main_loop_quit (info.loop);
     304                 :          1 :   g_thread_join (server_thread);
     305                 :            : 
     306         [ +  - ]:          1 :   if (tmpdir != NULL)
     307                 :          1 :     g_assert_no_errno (g_rmdir (tmpdir));
     308                 :            : 
     309                 :          1 :   g_clear_pointer (&info.ctx, g_main_context_unref);
     310                 :          1 :   g_clear_pointer (&info.loop, g_main_loop_unref);
     311                 :          1 :   g_clear_object (&addr);
     312                 :          1 :   g_string_free (buf, TRUE);
     313                 :          1 :   g_string_free (encoded_uid, TRUE);
     314                 :          1 :   g_string_free (encoded_wrong_uid, TRUE);
     315                 :          1 :   g_free (escaped);
     316                 :          1 :   g_free (info.guid);
     317                 :          1 :   g_free (info.listenable_address);
     318                 :          1 :   g_free (path);
     319                 :          1 :   g_free (tmpdir);
     320                 :          1 :   g_cond_clear (&info.cond);
     321                 :          1 :   g_mutex_clear (&info.mutex);
     322                 :          1 : }
     323                 :            : 
     324                 :            : int
     325                 :          1 : main (int   argc,
     326                 :            :       char *argv[])
     327                 :            : {
     328                 :          1 :   setlocale (LC_ALL, "");
     329                 :          1 :   g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
     330                 :            : 
     331                 :          1 :   g_test_add_func ("/gdbus/sasl/server", test_sasl_server);
     332                 :            : 
     333                 :          1 :   return g_test_run();
     334                 :            : }

Generated by: LCOV version 1.14