diff options
author | Matthias Clasen <mclasen@redhat.com> | 2010-05-13 23:08:34 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2010-05-13 23:08:34 -0400 |
commit | 6223341cacc7dfa34a8d60ec1b4828382dee6d07 (patch) | |
tree | 9969de8c5b3c6fb2307f197a47a9ff4f6a48a728 /gio/gunixconnection.c | |
parent | a7c4c7de58a48c179e4dc3336814f63c33ad07ff (diff) | |
parent | 6e8637e4783ae4e573f6784f005920930d9fca87 (diff) | |
download | glib-6223341cacc7dfa34a8d60ec1b4828382dee6d07.tar.gz |
Merge branch 'gdbus-merge'
Conflicts:
docs/reference/gio/gio-docs.xml
docs/reference/gio/gio-sections.txt
gio/tests/Makefile.am
Diffstat (limited to 'gio/gunixconnection.c')
-rw-r--r-- | gio/gunixconnection.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/gio/gunixconnection.c b/gio/gunixconnection.c index d56b3f873..3921c92d2 100644 --- a/gio/gunixconnection.c +++ b/gio/gunixconnection.c @@ -41,6 +41,14 @@ #include <gio/gsocket.h> #include <unistd.h> +#ifdef __linux__ +/* for getsockopt() and setsockopt() */ +#include <sys/types.h> /* See NOTES */ +#include <sys/socket.h> +#include <errno.h> +#include <string.h> +#endif + #include "gioalias.h" G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection, @@ -292,5 +300,253 @@ gboolean g_unix_connection_create_pair (GUnixCo GError **error); */ + +/** + * g_unix_connection_send_credentials: + * @connection: A #GUnixConnection. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Passes the credentials of the current user the receiving side + * of the connection. The recieving end has to call + * g_unix_connection_receive_credentials() (or similar) to accept the + * credentials. + * + * As well as sending the credentials this also writes a single NUL + * byte to the stream, as this is required for credentials passing to + * work on some implementations. + * + * Note that this function only works on Linux, currently. + * + * Returns: %TRUE on success, %FALSE if @error is set. + * + * Since: 2.26 + */ +gboolean +g_unix_connection_send_credentials (GUnixConnection *connection, + GCancellable *cancellable, + GError **error) +{ + GCredentials *credentials; + GSocketControlMessage *scm; + GSocket *socket; + gboolean ret; + GOutputVector vector; + guchar nul_byte[1] = {'\0'}; + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + + credentials = g_credentials_new (); + + vector.buffer = &nul_byte; + vector.size = 1; + scm = g_unix_credentials_message_new_with_credentials (credentials); + g_object_get (connection, "socket", &socket, NULL); + if (g_socket_send_message (socket, + NULL, /* address */ + &vector, + 1, + &scm, + 1, + G_SOCKET_MSG_NONE, + cancellable, + error) != 1) + { + g_prefix_error (error, _("Error sending credentials: ")); + goto out; + } + + ret = TRUE; + + out: + g_object_unref (socket); + g_object_unref (scm); + g_object_unref (credentials); + return ret; +} + +/** + * g_unix_connection_receive_credentials: + * @connection: A #GUnixConnection. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Receives credentials from the sending end of the connection. The + * sending end has to call g_unix_connection_send_credentials() (or + * similar) for this to work. + * + * As well as reading the credentials this also reads (and discards) a + * single byte from the stream, as this is required for credentials + * passing to work on some implementations. + * + * Returns: Received credentials on success (free with + * g_object_unref()), %NULL if @error is set. + * + * Since: 2.26 + */ +GCredentials * +g_unix_connection_receive_credentials (GUnixConnection *connection, + GCancellable *cancellable, + GError **error) +{ + GCredentials *ret; + GSocketControlMessage **scms; + gint nscm; + GSocket *socket; + gint n; + volatile GType credentials_message_gtype; + gssize num_bytes_read; +#ifdef __linux__ + gboolean turn_off_so_passcreds; +#endif + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = NULL; + scms = NULL; + + g_object_get (connection, "socket", &socket, NULL); + + /* On Linux, we need to turn on SO_PASSCRED if it isn't enabled + * already. We also need to turn it off when we're done. See + * #617483 for more discussion. + */ +#ifdef __linux__ + { + gint opt_val; + socklen_t opt_len; + + turn_off_so_passcreds = FALSE; + opt_val = 0; + opt_len = sizeof (gint); + if (getsockopt (g_socket_get_fd (socket), + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + &opt_len) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error checking if SO_PASSCRED is enabled for socket: %s"), + strerror (errno)); + goto out; + } + if (opt_len != sizeof (gint)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected option length while checking if SO_PASSCRED is enabled for socket. " + "Expected %d bytes, got %d"), + (gint) sizeof (gint), (gint) opt_len); + goto out; + } + if (opt_val == 0) + { + opt_val = 1; + if (setsockopt (g_socket_get_fd (socket), + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + sizeof opt_val) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error enabling SO_PASSCRED: %s"), + strerror (errno)); + goto out; + } + turn_off_so_passcreds = TRUE; + } + } +#endif + + /* ensure the type of GUnixCredentialsMessage has been registered with the type system */ + credentials_message_gtype = G_TYPE_UNIX_CREDENTIALS_MESSAGE; + num_bytes_read = g_socket_receive_message (socket, + NULL, /* GSocketAddress **address */ + NULL, + 0, + &scms, + &nscm, + NULL, + cancellable, + error); + if (num_bytes_read != 1) + { + /* Handle situation where g_socket_receive_message() returns + * 0 bytes and not setting @error + */ + if (num_bytes_read == 0 && error != NULL && *error == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Expecting to read a single byte for receiving credentials but read zero bytes")); + } + goto out; + } + + if (nscm != 1) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Expecting 1 control message, got %d"), + nscm); + goto out; + } + + if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0])) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected type of ancillary data")); + goto out; + } + + ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0])); + g_object_ref (ret); + + out: + +#ifdef __linux__ + if (turn_off_so_passcreds) + { + gint opt_val; + opt_val = 0; + if (setsockopt (g_socket_get_fd (socket), + SOL_SOCKET, + SO_PASSCRED, + &opt_val, + sizeof opt_val) != 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + _("Error while disabling SO_PASSCRED: %s"), + strerror (errno)); + goto out; + } + } +#endif + + if (scms != NULL) + { + for (n = 0; n < nscm; n++) + g_object_unref (scms[n]); + g_free (scms); + } + g_object_unref (socket); + return ret; +} + #define __G_UNIX_CONNECTION_C__ #include "gioaliasdef.c" |