(root)/
glib-2.79.0/
gio/
gunixcredentialsmessage.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   *
       3   * Copyright (C) 2010 Red Hat, Inc.
       4   * Copyright (C) 2009 Codethink Limited
       5   *
       6   * SPDX-License-Identifier: LGPL-2.1-or-later
       7   *
       8   * This library is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU Lesser General Public
      10   * License as published by the Free Software Foundation; either
      11   * version 2.1 of the License, or (at your option) any later version.
      12   *
      13   * See the included COPYING file for more information.
      14   *
      15   * Authors: David Zeuthen <davidz@redhat.com>
      16   */
      17  
      18  /**
      19   * GUnixCredentialsMessage:
      20   *
      21   * This [class@Gio.SocketControlMessage] contains a [class@Gio.Credentials]
      22   * instance.  It may be sent using [method@Gio.Socket.send_message] and received
      23   * using [method@Gio.Socket.receive_message] over UNIX sockets (ie: sockets in
      24   * the `G_SOCKET_FAMILY_UNIX` family).
      25   *
      26   * For an easier way to send and receive credentials over
      27   * stream-oriented UNIX sockets, see
      28   * [method@Gio.UnixConnection.send_credentials] and
      29   * [method@Gio.UnixConnection.receive_credentials]. To receive credentials of
      30   * a foreign process connected to a socket, use
      31   * [method@Gio.Socket.get_credentials].
      32   *
      33   * Since GLib 2.72, `GUnixCredentialMessage` is available on all platforms. It
      34   * requires underlying system support (such as Windows 10 with `AF_UNIX`) at run
      35   * time.
      36   *
      37   * Before GLib 2.72, `<gio/gunixcredentialsmessage.h>` belonged to the UNIX-specific
      38   * GIO interfaces, thus you had to use the `gio-unix-2.0.pc` pkg-config file
      39   * when using it. This is no longer necessary since GLib 2.72.
      40   *
      41   * Since: 2.26
      42   */
      43  
      44  #include "config.h"
      45  
      46  /* ---------------------------------------------------------------------------------------------------- */
      47  
      48  #include <fcntl.h>
      49  #include <errno.h>
      50  #include <string.h>
      51  #ifdef HAVE_UNISTD_H
      52  #include <unistd.h>
      53  #endif
      54  
      55  #include "gunixcredentialsmessage.h"
      56  #include "gcredentials.h"
      57  #include "gcredentialsprivate.h"
      58  #include "gnetworking.h"
      59  
      60  #include "glibintl.h"
      61  
      62  struct _GUnixCredentialsMessagePrivate
      63  {
      64    GCredentials *credentials;
      65  };
      66  
      67  enum
      68  {
      69    PROP_0,
      70    PROP_CREDENTIALS
      71  };
      72  
      73  G_DEFINE_TYPE_WITH_PRIVATE (GUnixCredentialsMessage, g_unix_credentials_message, G_TYPE_SOCKET_CONTROL_MESSAGE)
      74  
      75  static gsize
      76  g_unix_credentials_message_get_size (GSocketControlMessage *message)
      77  {
      78  #if G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
      79    return G_CREDENTIALS_NATIVE_SIZE;
      80  #else
      81    return 0;
      82  #endif
      83  }
      84  
      85  static int
      86  g_unix_credentials_message_get_level (GSocketControlMessage *message)
      87  {
      88  #if G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
      89    return SOL_SOCKET;
      90  #else
      91    return 0;
      92  #endif
      93  }
      94  
      95  static int
      96  g_unix_credentials_message_get_msg_type (GSocketControlMessage *message)
      97  {
      98  #if G_CREDENTIALS_USE_LINUX_UCRED
      99    return SCM_CREDENTIALS;
     100  #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
     101    return SCM_CREDS;
     102  #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
     103    return SCM_CREDS;
     104  #elif G_CREDENTIALS_USE_SOLARIS_UCRED
     105    return SCM_UCRED;
     106  #elif G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
     107    #error "G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED is set but there is no msg_type defined for this platform"
     108  #else
     109    /* includes G_CREDENTIALS_USE_APPLE_XUCRED */
     110    return 0;
     111  #endif
     112  }
     113  
     114  static GSocketControlMessage *
     115  g_unix_credentials_message_deserialize (gint     level,
     116                                          gint     type,
     117                                          gsize    size,
     118                                          gpointer data)
     119  {
     120  #if G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
     121    GSocketControlMessage *message;
     122    GCredentials *credentials;
     123  
     124    if (level != SOL_SOCKET || type != g_unix_credentials_message_get_msg_type (NULL))
     125      return NULL;
     126  
     127    if (size != G_CREDENTIALS_NATIVE_SIZE)
     128      {
     129        g_warning ("Expected a credentials struct of %" G_GSIZE_FORMAT " bytes but "
     130                   "got %" G_GSIZE_FORMAT " bytes of data",
     131                   G_CREDENTIALS_NATIVE_SIZE, size);
     132        return NULL;
     133      }
     134  
     135    credentials = g_credentials_new ();
     136    g_credentials_set_native (credentials, G_CREDENTIALS_NATIVE_TYPE, data);
     137  
     138    if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
     139      {
     140        /* This happens on Linux if the remote side didn't pass the credentials */
     141        g_object_unref (credentials);
     142        return NULL;
     143      }
     144  
     145    message = g_unix_credentials_message_new_with_credentials (credentials);
     146    g_object_unref (credentials);
     147  
     148    return message;
     149  
     150  #else /* !G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED */
     151  
     152    return NULL;
     153  #endif
     154  }
     155  
     156  static void
     157  g_unix_credentials_message_serialize (GSocketControlMessage *_message,
     158                                        gpointer               data)
     159  {
     160  #if G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
     161    GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (_message);
     162  
     163    memcpy (data,
     164            g_credentials_get_native (message->priv->credentials,
     165                                      G_CREDENTIALS_NATIVE_TYPE),
     166            G_CREDENTIALS_NATIVE_SIZE);
     167  #endif
     168  }
     169  
     170  static void
     171  g_unix_credentials_message_finalize (GObject *object)
     172  {
     173    GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
     174  
     175    if (message->priv->credentials != NULL)
     176      g_object_unref (message->priv->credentials);
     177  
     178    G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->finalize (object);
     179  }
     180  
     181  static void
     182  g_unix_credentials_message_init (GUnixCredentialsMessage *message)
     183  {
     184    message->priv = g_unix_credentials_message_get_instance_private (message);
     185  }
     186  
     187  static void
     188  g_unix_credentials_message_get_property (GObject    *object,
     189                                           guint       prop_id,
     190                                           GValue     *value,
     191                                           GParamSpec *pspec)
     192  {
     193    GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
     194  
     195    switch (prop_id)
     196      {
     197      case PROP_CREDENTIALS:
     198        g_value_set_object (value, message->priv->credentials);
     199        break;
     200  
     201      default:
     202        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     203        break;
     204      }
     205  }
     206  
     207  static void
     208  g_unix_credentials_message_set_property (GObject      *object,
     209                                           guint         prop_id,
     210                                           const GValue *value,
     211                                           GParamSpec   *pspec)
     212  {
     213    GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
     214  
     215    switch (prop_id)
     216      {
     217      case PROP_CREDENTIALS:
     218        message->priv->credentials = g_value_dup_object (value);
     219        break;
     220  
     221      default:
     222        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     223        break;
     224      }
     225  }
     226  
     227  static void
     228  g_unix_credentials_message_constructed (GObject *object)
     229  {
     230    GUnixCredentialsMessage *message = G_UNIX_CREDENTIALS_MESSAGE (object);
     231  
     232    if (message->priv->credentials == NULL)
     233      message->priv->credentials = g_credentials_new ();
     234  
     235    if (G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed != NULL)
     236      G_OBJECT_CLASS (g_unix_credentials_message_parent_class)->constructed (object);
     237  }
     238  
     239  static void
     240  g_unix_credentials_message_class_init (GUnixCredentialsMessageClass *class)
     241  {
     242    GSocketControlMessageClass *scm_class;
     243    GObjectClass *gobject_class;
     244  
     245    gobject_class = G_OBJECT_CLASS (class);
     246    gobject_class->get_property = g_unix_credentials_message_get_property;
     247    gobject_class->set_property = g_unix_credentials_message_set_property;
     248    gobject_class->finalize = g_unix_credentials_message_finalize;
     249    gobject_class->constructed = g_unix_credentials_message_constructed;
     250  
     251    scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
     252    scm_class->get_size = g_unix_credentials_message_get_size;
     253    scm_class->get_level = g_unix_credentials_message_get_level;
     254    scm_class->get_type = g_unix_credentials_message_get_msg_type;
     255    scm_class->serialize = g_unix_credentials_message_serialize;
     256    scm_class->deserialize = g_unix_credentials_message_deserialize;
     257  
     258    /**
     259     * GUnixCredentialsMessage:credentials:
     260     *
     261     * The credentials stored in the message.
     262     *
     263     * Since: 2.26
     264     */
     265    g_object_class_install_property (gobject_class,
     266                                     PROP_CREDENTIALS,
     267                                     g_param_spec_object ("credentials", NULL, NULL,
     268                                                          G_TYPE_CREDENTIALS,
     269                                                          G_PARAM_READABLE |
     270                                                          G_PARAM_WRITABLE |
     271                                                          G_PARAM_CONSTRUCT_ONLY |
     272                                                          G_PARAM_STATIC_NAME |
     273                                                          G_PARAM_STATIC_BLURB |
     274                                                          G_PARAM_STATIC_NICK));
     275  
     276  }
     277  
     278  /* ---------------------------------------------------------------------------------------------------- */
     279  
     280  /**
     281   * g_unix_credentials_message_is_supported:
     282   *
     283   * Checks if passing #GCredentials on a #GSocket is supported on this platform.
     284   *
     285   * Returns: %TRUE if supported, %FALSE otherwise
     286   *
     287   * Since: 2.26
     288   */
     289  gboolean
     290  g_unix_credentials_message_is_supported (void)
     291  {
     292  #if G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
     293    return TRUE;
     294  #else
     295    return FALSE;
     296  #endif
     297  }
     298  
     299  /* ---------------------------------------------------------------------------------------------------- */
     300  
     301  /**
     302   * g_unix_credentials_message_new:
     303   *
     304   * Creates a new #GUnixCredentialsMessage with credentials matching the current processes.
     305   *
     306   * Returns: a new #GUnixCredentialsMessage
     307   *
     308   * Since: 2.26
     309   */
     310  GSocketControlMessage *
     311  g_unix_credentials_message_new (void)
     312  {
     313    g_return_val_if_fail (g_unix_credentials_message_is_supported (), NULL);
     314    return g_object_new (G_TYPE_UNIX_CREDENTIALS_MESSAGE,
     315                         NULL);
     316  }
     317  
     318  /**
     319   * g_unix_credentials_message_new_with_credentials:
     320   * @credentials: A #GCredentials object.
     321   *
     322   * Creates a new #GUnixCredentialsMessage holding @credentials.
     323   *
     324   * Returns: a new #GUnixCredentialsMessage
     325   *
     326   * Since: 2.26
     327   */
     328  GSocketControlMessage *
     329  g_unix_credentials_message_new_with_credentials (GCredentials *credentials)
     330  {
     331    g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
     332    g_return_val_if_fail (g_unix_credentials_message_is_supported (), NULL);
     333    return g_object_new (G_TYPE_UNIX_CREDENTIALS_MESSAGE,
     334                         "credentials", credentials,
     335                         NULL);
     336  }
     337  
     338  /**
     339   * g_unix_credentials_message_get_credentials:
     340   * @message: A #GUnixCredentialsMessage.
     341   *
     342   * Gets the credentials stored in @message.
     343   *
     344   * Returns: (transfer none): A #GCredentials instance. Do not free, it is owned by @message.
     345   *
     346   * Since: 2.26
     347   */
     348  GCredentials *
     349  g_unix_credentials_message_get_credentials (GUnixCredentialsMessage *message)
     350  {
     351    g_return_val_if_fail (G_IS_UNIX_CREDENTIALS_MESSAGE (message), NULL);
     352    return message->priv->credentials;
     353  }