(root)/
glib-2.79.0/
gio/
gunixconnection.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   *
       3   * Copyright © 2009 Codethink Limited
       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   * See the included COPYING file for more information.
      13   *
      14   * Authors: Ryan Lortie <desrt@desrt.ca>
      15   */
      16  
      17  #include "config.h"
      18  
      19  #include "gunixconnection.h"
      20  #include "gnetworking.h"
      21  #include "gsocket.h"
      22  #include "gsocketcontrolmessage.h"
      23  #include "gunixcredentialsmessage.h"
      24  #include "gunixfdmessage.h"
      25  #include "glibintl.h"
      26  
      27  #include <errno.h>
      28  #include <string.h>
      29  #ifdef HAVE_UNISTD_H
      30  #include <unistd.h>
      31  #endif
      32  
      33  /**
      34   * GUnixConnection:
      35   *
      36   * This is the subclass of [class@Gio.SocketConnection] that is created
      37   * for UNIX domain sockets.
      38   *
      39   * It contains functions to do some of the UNIX socket specific
      40   * functionality like passing file descriptors.
      41   *
      42   * Since GLib 2.72, `GUnixConnection` is available on all platforms. It requires
      43   * underlying system support (such as Windows 10 with `AF_UNIX`) at run time.
      44   *
      45   * Before GLib 2.72, `<gio/gunixconnection.h>` belonged to the UNIX-specific GIO
      46   * interfaces, thus you had to use the `gio-unix-2.0.pc` pkg-config file when
      47   * using it. This is no longer necessary since GLib 2.72.
      48   *
      49   * Since: 2.22
      50   */
      51  
      52  G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection,
      53  			 G_TYPE_SOCKET_CONNECTION,
      54    g_socket_connection_factory_register_type (g_define_type_id,
      55  					     G_SOCKET_FAMILY_UNIX,
      56  					     G_SOCKET_TYPE_STREAM,
      57  					     G_SOCKET_PROTOCOL_DEFAULT);
      58  			 );
      59  
      60  /**
      61   * g_unix_connection_send_fd:
      62   * @connection: a #GUnixConnection
      63   * @fd: a file descriptor
      64   * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
      65   * @error: (nullable): #GError for error reporting, or %NULL to ignore.
      66   *
      67   * Passes a file descriptor to the receiving side of the
      68   * connection. The receiving end has to call g_unix_connection_receive_fd()
      69   * to accept the file descriptor.
      70   *
      71   * As well as sending the fd this also writes a single byte to the
      72   * stream, as this is required for fd passing to work on some
      73   * implementations.
      74   *
      75   * Returns: a %TRUE on success, %NULL on error.
      76   *
      77   * Since: 2.22
      78   */
      79  gboolean
      80  g_unix_connection_send_fd (GUnixConnection  *connection,
      81                             gint              fd,
      82                             GCancellable     *cancellable,
      83                             GError          **error)
      84  {
      85  #ifdef G_OS_UNIX
      86    GSocketControlMessage *scm;
      87    GSocket *socket;
      88  
      89    g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE);
      90    g_return_val_if_fail (fd >= 0, FALSE);
      91  
      92    scm = g_unix_fd_message_new ();
      93  
      94    if (!g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (scm), fd, error))
      95      {
      96        g_object_unref (scm);
      97        return FALSE;
      98      }
      99  
     100    g_object_get (connection, "socket", &socket, NULL);
     101    if (g_socket_send_message (socket, NULL, NULL, 0, &scm, 1, 0, cancellable, error) != 1)
     102      /* XXX could it 'fail' with zero? */
     103      {
     104        g_object_unref (socket);
     105        g_object_unref (scm);
     106  
     107        return FALSE;
     108      }
     109  
     110    g_object_unref (socket);
     111    g_object_unref (scm);
     112  
     113    return TRUE;
     114  #else
     115    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     116                         _("Sending FD is not supported"));
     117    return FALSE;
     118  #endif
     119  }
     120  
     121  /**
     122   * g_unix_connection_receive_fd:
     123   * @connection: a #GUnixConnection
     124   * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
     125   * @error: (nullable): #GError for error reporting, or %NULL to ignore
     126   *
     127   * Receives a file descriptor from the sending end of the connection.
     128   * The sending end has to call g_unix_connection_send_fd() for this
     129   * to work.
     130   *
     131   * As well as reading the fd this also reads a single byte from the
     132   * stream, as this is required for fd passing to work on some
     133   * implementations.
     134   *
     135   * Returns: a file descriptor on success, -1 on error.
     136   *
     137   * Since: 2.22
     138   **/
     139  gint
     140  g_unix_connection_receive_fd (GUnixConnection  *connection,
     141                                GCancellable     *cancellable,
     142                                GError          **error)
     143  {
     144  #ifdef G_OS_UNIX
     145    GSocketControlMessage **scms;
     146    gint *fds, nfd, fd, nscm;
     147    GUnixFDMessage *fdmsg;
     148    GSocket *socket;
     149  
     150    g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), -1);
     151  
     152    g_object_get (connection, "socket", &socket, NULL);
     153    if (g_socket_receive_message (socket, NULL, NULL, 0,
     154                                  &scms, &nscm, NULL, cancellable, error) != 1)
     155      /* XXX it _could_ 'fail' with zero. */
     156      {
     157        g_object_unref (socket);
     158  
     159        return -1;
     160      }
     161  
     162    g_object_unref (socket);
     163  
     164    if (nscm != 1)
     165      {
     166        gint i;
     167  
     168        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     169                     g_dngettext (NULL,
     170                                  "Expecting 1 control message, got %d",
     171                                  "Expecting 1 control message, got %d",
     172                                  nscm),
     173                     nscm);
     174  
     175        for (i = 0; i < nscm; i++)
     176          g_object_unref (scms[i]);
     177  
     178        g_free (scms);
     179  
     180        return -1;
     181      }
     182  
     183    if (!G_IS_UNIX_FD_MESSAGE (scms[0]))
     184      {
     185        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     186  			   _("Unexpected type of ancillary data"));
     187        g_object_unref (scms[0]);
     188        g_free (scms);
     189  
     190        return -1;
     191      }
     192  
     193    fdmsg = G_UNIX_FD_MESSAGE (scms[0]);
     194    g_free (scms);
     195  
     196    fds = g_unix_fd_message_steal_fds (fdmsg, &nfd);
     197    g_object_unref (fdmsg);
     198  
     199    if (nfd != 1)
     200      {
     201        gint i;
     202  
     203        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     204                     g_dngettext (NULL,
     205                                  "Expecting one fd, but got %d\n",
     206                                  "Expecting one fd, but got %d\n",
     207                                  nfd),
     208                     nfd);
     209  
     210        for (i = 0; i < nfd; i++)
     211          close (fds[i]);
     212  
     213        g_free (fds);
     214  
     215        return -1;
     216      }
     217  
     218    fd = *fds;
     219    g_free (fds);
     220  
     221    if (fd < 0)
     222      {
     223        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     224                             _("Received invalid fd"));
     225        fd = -1;
     226      }
     227  
     228    return fd;
     229  #else
     230    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     231                         _("Receiving FD is not supported"));
     232    return -1;
     233  #endif
     234  }
     235  
     236  static void
     237  g_unix_connection_init (GUnixConnection *connection)
     238  {
     239  }
     240  
     241  static void
     242  g_unix_connection_class_init (GUnixConnectionClass *class)
     243  {
     244  }
     245  
     246  /* TODO: Other stuff we might want to add are:
     247  void                    g_unix_connection_send_fd_async                 (GUnixConnection      *connection,
     248                                                                           gint                  fd,
     249                                                                           gboolean              close,
     250                                                                           gint                  io_priority,
     251                                                                           GAsyncReadyCallback   callback,
     252                                                                           gpointer              user_data);
     253  gboolean                g_unix_connection_send_fd_finish                (GUnixConnection      *connection,
     254                                                                           GError              **error);
     255  
     256  gboolean                g_unix_connection_send_fds                      (GUnixConnection      *connection,
     257                                                                           gint                 *fds,
     258                                                                           gint                  nfds,
     259                                                                           GError              **error);
     260  void                    g_unix_connection_send_fds_async                (GUnixConnection      *connection,
     261                                                                           gint                 *fds,
     262                                                                           gint                  nfds,
     263                                                                           gint                  io_priority,
     264                                                                           GAsyncReadyCallback   callback,
     265                                                                           gpointer              user_data);
     266  gboolean                g_unix_connection_send_fds_finish               (GUnixConnection      *connection,
     267                                                                           GError              **error);
     268  
     269  void                    g_unix_connection_receive_fd_async              (GUnixConnection      *connection,
     270                                                                           gint                  io_priority,
     271                                                                           GAsyncReadyCallback   callback,
     272                                                                           gpointer              user_data);
     273  gint                    g_unix_connection_receive_fd_finish             (GUnixConnection      *connection,
     274                                                                           GError              **error);
     275  
     276  
     277  gboolean                g_unix_connection_send_fake_credentials         (GUnixConnection      *connection,
     278                                                                           guint64               pid,
     279                                                                           guint64               uid,
     280                                                                           guint64               gid,
     281                                                                           GError              **error);
     282  void                    g_unix_connection_send_fake_credentials_async   (GUnixConnection      *connection,
     283                                                                           guint64               pid,
     284                                                                           guint64               uid,
     285                                                                           guint64               gid,
     286                                                                           gint                  io_priority,
     287                                                                           GAsyncReadyCallback   callback,
     288                                                                           gpointer              user_data);
     289  gboolean                g_unix_connection_send_fake_credentials_finish  (GUnixConnection      *connection,
     290                                                                           GError              **error);
     291  
     292  gboolean                g_unix_connection_create_pair                   (GUnixConnection     **one,
     293                                                                           GUnixConnection     **two,
     294                                                                           GError              **error);
     295  */
     296  
     297  
     298  /**
     299   * g_unix_connection_send_credentials:
     300   * @connection: A #GUnixConnection.
     301   * @cancellable: (nullable): A #GCancellable or %NULL.
     302   * @error: Return location for error or %NULL.
     303   *
     304   * Passes the credentials of the current user the receiving side
     305   * of the connection. The receiving end has to call
     306   * g_unix_connection_receive_credentials() (or similar) to accept the
     307   * credentials.
     308   *
     309   * As well as sending the credentials this also writes a single NUL
     310   * byte to the stream, as this is required for credentials passing to
     311   * work on some implementations.
     312   *
     313   * This method can be expected to be available on the following platforms:
     314   *
     315   * - Linux since GLib 2.26
     316   * - FreeBSD since GLib 2.26
     317   * - GNU/kFreeBSD since GLib 2.36
     318   * - Solaris, Illumos and OpenSolaris since GLib 2.40
     319   * - GNU/Hurd since GLib 2.40
     320   *
     321   * Other ways to exchange credentials with a foreign peer includes the
     322   * #GUnixCredentialsMessage type and g_socket_get_credentials() function.
     323   *
     324   * Returns: %TRUE on success, %FALSE if @error is set.
     325   *
     326   * Since: 2.26
     327   */
     328  gboolean
     329  g_unix_connection_send_credentials (GUnixConnection      *connection,
     330                                      GCancellable         *cancellable,
     331                                      GError              **error)
     332  {
     333    GCredentials *credentials;
     334    GSocketControlMessage *scm;
     335    GSocket *socket;
     336    gboolean ret;
     337    GOutputVector vector;
     338    guchar nul_byte[1] = {'\0'};
     339    gint num_messages;
     340  
     341    g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE);
     342    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
     343  
     344    ret = FALSE;
     345  
     346    credentials = g_credentials_new ();
     347  
     348    vector.buffer = &nul_byte;
     349    vector.size = 1;
     350  
     351    if (g_unix_credentials_message_is_supported ())
     352      {
     353        scm = g_unix_credentials_message_new_with_credentials (credentials);
     354        num_messages = 1;
     355      }
     356    else
     357      {
     358        scm = NULL;
     359        num_messages = 0;
     360      }
     361  
     362    g_object_get (connection, "socket", &socket, NULL);
     363    if (g_socket_send_message (socket,
     364                               NULL, /* address */
     365                               &vector,
     366                               1,
     367                               &scm,
     368                               num_messages,
     369                               G_SOCKET_MSG_NONE,
     370                               cancellable,
     371                               error) != 1)
     372      {
     373        g_prefix_error (error, _("Error sending credentials: "));
     374        goto out;
     375      }
     376  
     377    ret = TRUE;
     378  
     379   out:
     380    g_object_unref (socket);
     381    if (scm != NULL)
     382      g_object_unref (scm);
     383    g_object_unref (credentials);
     384    return ret;
     385  }
     386  
     387  static void
     388  send_credentials_async_thread (GTask         *task,
     389  			       gpointer       source_object,
     390  			       gpointer       task_data,
     391  			       GCancellable  *cancellable)
     392  {
     393    GError *error = NULL;
     394  
     395    if (g_unix_connection_send_credentials (G_UNIX_CONNECTION (source_object),
     396  					  cancellable,
     397  					  &error))
     398      g_task_return_boolean (task, TRUE);
     399    else
     400      g_task_return_error (task, error);
     401    g_object_unref (task);
     402  }
     403  
     404  /**
     405   * g_unix_connection_send_credentials_async:
     406   * @connection: A #GUnixConnection.
     407   * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
     408   * @callback: (scope async): a #GAsyncReadyCallback
     409   *   to call when the request is satisfied
     410   * @user_data: the data to pass to callback function
     411   *
     412   * Asynchronously send credentials.
     413   *
     414   * For more details, see g_unix_connection_send_credentials() which is
     415   * the synchronous version of this call.
     416   *
     417   * When the operation is finished, @callback will be called. You can then call
     418   * g_unix_connection_send_credentials_finish() to get the result of the operation.
     419   *
     420   * Since: 2.32
     421   **/
     422  void
     423  g_unix_connection_send_credentials_async (GUnixConnection      *connection,
     424                                            GCancellable         *cancellable,
     425                                            GAsyncReadyCallback   callback,
     426                                            gpointer              user_data)
     427  {
     428    GTask *task;
     429  
     430    task = g_task_new (connection, cancellable, callback, user_data);
     431    g_task_set_source_tag (task, g_unix_connection_send_credentials_async);
     432    g_task_run_in_thread (task, send_credentials_async_thread);
     433  }
     434  
     435  /**
     436   * g_unix_connection_send_credentials_finish:
     437   * @connection: A #GUnixConnection.
     438   * @result: a #GAsyncResult.
     439   * @error: a #GError, or %NULL
     440   *
     441   * Finishes an asynchronous send credentials operation started with
     442   * g_unix_connection_send_credentials_async().
     443   *
     444   * Returns: %TRUE if the operation was successful, otherwise %FALSE.
     445   *
     446   * Since: 2.32
     447   **/
     448  gboolean
     449  g_unix_connection_send_credentials_finish (GUnixConnection *connection,
     450                                             GAsyncResult    *result,
     451                                             GError         **error)
     452  {
     453    g_return_val_if_fail (g_task_is_valid (result, connection), FALSE);
     454  
     455    return g_task_propagate_boolean (G_TASK (result), error);
     456  }
     457  
     458  /**
     459   * g_unix_connection_receive_credentials:
     460   * @connection: A #GUnixConnection.
     461   * @cancellable: (nullable): A #GCancellable or %NULL.
     462   * @error: Return location for error or %NULL.
     463   *
     464   * Receives credentials from the sending end of the connection.  The
     465   * sending end has to call g_unix_connection_send_credentials() (or
     466   * similar) for this to work.
     467   *
     468   * As well as reading the credentials this also reads (and discards) a
     469   * single byte from the stream, as this is required for credentials
     470   * passing to work on some implementations.
     471   *
     472   * This method can be expected to be available on the following platforms:
     473   *
     474   * - Linux since GLib 2.26
     475   * - FreeBSD since GLib 2.26
     476   * - GNU/kFreeBSD since GLib 2.36
     477   * - Solaris, Illumos and OpenSolaris since GLib 2.40
     478   * - GNU/Hurd since GLib 2.40
     479   *
     480   * Other ways to exchange credentials with a foreign peer includes the
     481   * #GUnixCredentialsMessage type and g_socket_get_credentials() function.
     482   *
     483   * Returns: (transfer full): Received credentials on success (free with
     484   * g_object_unref()), %NULL if @error is set.
     485   *
     486   * Since: 2.26
     487   */
     488  GCredentials *
     489  g_unix_connection_receive_credentials (GUnixConnection      *connection,
     490                                         GCancellable         *cancellable,
     491                                         GError              **error)
     492  {
     493    GCredentials *ret;
     494    GSocketControlMessage **scms;
     495    gint nscm;
     496    GSocket *socket;
     497    gint n;
     498    gssize num_bytes_read;
     499  #ifdef __linux__
     500    gboolean turn_off_so_passcreds;
     501  #endif
     502  
     503    g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL);
     504    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     505  
     506    ret = NULL;
     507    scms = NULL;
     508  
     509    g_object_get (connection, "socket", &socket, NULL);
     510  
     511    /* On Linux, we need to turn on SO_PASSCRED if it isn't enabled
     512     * already. We also need to turn it off when we're done.  See
     513     * #617483 for more discussion.
     514     */
     515  #ifdef __linux__
     516    {
     517      gint opt_val;
     518  
     519      turn_off_so_passcreds = FALSE;
     520      opt_val = 0;
     521      if (!g_socket_get_option (socket,
     522  			      SOL_SOCKET,
     523  			      SO_PASSCRED,
     524  			      &opt_val,
     525  			      NULL))
     526        {
     527          int errsv = errno;
     528          g_set_error (error,
     529                       G_IO_ERROR,
     530                       g_io_error_from_errno (errsv),
     531                       _("Error checking if SO_PASSCRED is enabled for socket: %s"),
     532                       g_strerror (errsv));
     533          goto out;
     534        }
     535      if (opt_val == 0)
     536        {
     537          if (!g_socket_set_option (socket,
     538  				  SOL_SOCKET,
     539  				  SO_PASSCRED,
     540  				  TRUE,
     541  				  NULL))
     542            {
     543              int errsv = errno;
     544              g_set_error (error,
     545                           G_IO_ERROR,
     546                           g_io_error_from_errno (errsv),
     547                           _("Error enabling SO_PASSCRED: %s"),
     548                           g_strerror (errsv));
     549              goto out;
     550            }
     551          turn_off_so_passcreds = TRUE;
     552        }
     553    }
     554  #endif
     555  
     556    g_type_ensure (G_TYPE_UNIX_CREDENTIALS_MESSAGE);
     557    num_bytes_read = g_socket_receive_message (socket,
     558                                               NULL, /* GSocketAddress **address */
     559                                               NULL,
     560                                               0,
     561                                               &scms,
     562                                               &nscm,
     563                                               NULL,
     564                                               cancellable,
     565                                               error);
     566    if (num_bytes_read != 1)
     567      {
     568        /* Handle situation where g_socket_receive_message() returns
     569         * 0 bytes and not setting @error
     570         */
     571        if (num_bytes_read == 0 && error != NULL && *error == NULL)
     572          {
     573            g_set_error_literal (error,
     574                                 G_IO_ERROR,
     575                                 G_IO_ERROR_FAILED,
     576                                 _("Expecting to read a single byte for receiving credentials but read zero bytes"));
     577          }
     578        goto out;
     579      }
     580  
     581    if (g_unix_credentials_message_is_supported () &&
     582        /* Fall back on get_credentials if the other side didn't send the credentials */
     583        nscm > 0)
     584      {
     585        if (nscm != 1)
     586          {
     587            g_set_error (error,
     588                         G_IO_ERROR,
     589                         G_IO_ERROR_FAILED,
     590                         g_dngettext (NULL,
     591                                      "Expecting 1 control message, got %d",
     592                                      "Expecting 1 control message, got %d",
     593                                      nscm),
     594                         nscm);
     595            goto out;
     596          }
     597  
     598        if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0]))
     599          {
     600            g_set_error_literal (error,
     601                                 G_IO_ERROR,
     602                                 G_IO_ERROR_FAILED,
     603                                 _("Unexpected type of ancillary data"));
     604            goto out;
     605          }
     606  
     607        ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0]));
     608        g_object_ref (ret);
     609      }
     610    else
     611      {
     612        if (nscm != 0)
     613          {
     614            g_set_error (error,
     615                         G_IO_ERROR,
     616                         G_IO_ERROR_FAILED,
     617                         _("Not expecting control message, but got %d"),
     618                         nscm);
     619            goto out;
     620          }
     621        else
     622          {
     623            ret = g_socket_get_credentials (socket, error);
     624          }
     625      }
     626  
     627   out:
     628  
     629  #ifdef __linux__
     630    if (turn_off_so_passcreds)
     631      {
     632        if (!g_socket_set_option (socket,
     633  				SOL_SOCKET,
     634  				SO_PASSCRED,
     635  				FALSE,
     636  				NULL))
     637          {
     638            int errsv = errno;
     639            g_set_error (error,
     640                         G_IO_ERROR,
     641                         g_io_error_from_errno (errsv),
     642                         _("Error while disabling SO_PASSCRED: %s"),
     643                         g_strerror (errsv));
     644            goto out;
     645          }
     646      }
     647  #endif
     648  
     649    if (scms != NULL)
     650      {
     651        for (n = 0; n < nscm; n++)
     652          g_object_unref (scms[n]);
     653        g_free (scms);
     654      }
     655    g_object_unref (socket);
     656    return ret;
     657  }
     658  
     659  static void
     660  receive_credentials_async_thread (GTask         *task,
     661  				  gpointer       source_object,
     662  				  gpointer       task_data,
     663  				  GCancellable  *cancellable)
     664  {
     665    GCredentials *creds;
     666    GError *error = NULL;
     667  
     668    creds = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (source_object),
     669                                                   cancellable,
     670                                                   &error);
     671    if (creds)
     672      g_task_return_pointer (task, creds, g_object_unref);
     673    else
     674      g_task_return_error (task, error);
     675    g_object_unref (task);
     676  }
     677  
     678  /**
     679   * g_unix_connection_receive_credentials_async:
     680   * @connection: A #GUnixConnection.
     681   * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
     682   * @callback: (scope async): a #GAsyncReadyCallback
     683   *   to call when the request is satisfied
     684   * @user_data: the data to pass to callback function
     685   *
     686   * Asynchronously receive credentials.
     687   *
     688   * For more details, see g_unix_connection_receive_credentials() which is
     689   * the synchronous version of this call.
     690   *
     691   * When the operation is finished, @callback will be called. You can then call
     692   * g_unix_connection_receive_credentials_finish() to get the result of the operation.
     693   *
     694   * Since: 2.32
     695   **/
     696  void
     697  g_unix_connection_receive_credentials_async (GUnixConnection      *connection,
     698                                                GCancellable         *cancellable,
     699                                                GAsyncReadyCallback   callback,
     700                                                gpointer              user_data)
     701  {
     702    GTask *task;
     703  
     704    task = g_task_new (connection, cancellable, callback, user_data);
     705    g_task_set_source_tag (task, g_unix_connection_receive_credentials_async);
     706    g_task_run_in_thread (task, receive_credentials_async_thread);
     707  }
     708  
     709  /**
     710   * g_unix_connection_receive_credentials_finish:
     711   * @connection: A #GUnixConnection.
     712   * @result: a #GAsyncResult.
     713   * @error: a #GError, or %NULL
     714   *
     715   * Finishes an asynchronous receive credentials operation started with
     716   * g_unix_connection_receive_credentials_async().
     717   *
     718   * Returns: (transfer full): a #GCredentials, or %NULL on error.
     719   *     Free the returned object with g_object_unref().
     720   *
     721   * Since: 2.32
     722   **/
     723  GCredentials *
     724  g_unix_connection_receive_credentials_finish (GUnixConnection *connection,
     725                                                GAsyncResult    *result,
     726                                                GError         **error)
     727  {
     728    g_return_val_if_fail (g_task_is_valid (result, connection), NULL);
     729  
     730    return g_task_propagate_pointer (G_TASK (result), error);
     731  }