LCOV - code coverage report
Current view: top level - gio/tests - gdbus-sasl.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 99.2 % 127 126
Test Date: 2024-11-26 05:23:01 Functions: 100.0 % 5 5
Branches: - 0 0

             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 2.0-1