From 5a269e5a90181a5cafce90e7e5d7bc4f42f47f52 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Thu, 19 Sep 2013 16:09:38 -0400 Subject: [PATCH] gcredentials: add Solaris support Based on patches from Igor Pashev. https://bugzilla.gnome.org/show_bug.cgi?id=705029 --- gio/gcredentials.c | 52 ++++++++++++++++++++++++++++++++--- gio/gcredentialsprivate.h | 12 ++++++++ gio/gioenums.h | 4 ++- gio/gsocket.c | 36 +++++++++++++++++++----- gio/gunixcredentialsmessage.c | 2 ++ gio/tests/credentials.c | 18 ++++++++++++ 6 files changed, 112 insertions(+), 12 deletions(-) diff --git a/gio/gcredentials.c b/gio/gcredentials.c index b18ccc40f..8e9f28a02 100644 --- a/gio/gcredentials.c +++ b/gio/gcredentials.c @@ -65,6 +65,10 @@ * * On OpenBSD, the native credential type is a struct sockpeercred. * This corresponds to %G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED. + * + * On Solaris (including OpenSolaris and its derivatives), the native + * credential type is a ucred_t. This corresponds to + * %G_CREDENTIALS_TYPE_SOLARIS_UCRED. */ /** @@ -86,6 +90,8 @@ struct _GCredentials struct cmsgcred native; #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED struct sockpeercred native; +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + ucred_t *native; #else #ifdef __GNUC__ #warning Please add GCredentials support for your OS @@ -111,7 +117,11 @@ G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT); static void g_credentials_finalize (GObject *object) { - G_GNUC_UNUSED GCredentials *credentials = G_CREDENTIALS (object); +#if G_CREDENTIALS_USE_SOLARIS_UCRED + GCredentials *credentials = G_CREDENTIALS (object); + + ucred_free (credentials->native); +#endif if (G_OBJECT_CLASS (g_credentials_parent_class)->finalize != NULL) G_OBJECT_CLASS (g_credentials_parent_class)->finalize (object); @@ -143,6 +153,8 @@ g_credentials_init (GCredentials *credentials) credentials->native.pid = getpid (); credentials->native.uid = geteuid (); credentials->native.gid = getegid (); +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + credentials->native = ucred_get (P_MYID); #endif } @@ -214,6 +226,19 @@ g_credentials_to_string (GCredentials *credentials) g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.gid); if (ret->str[ret->len - 1] == ',') ret->str[ret->len - 1] = '\0'; +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + g_string_append (ret, "solaris-ucred:"); + { + id_t id; + if ((id = ucred_getpid (credentials->native)) != -1) + g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) id); + if ((id = ucred_geteuid (credentials->native)) != -1) + g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) id); + if ((id = ucred_getegid (credentials->native)) != -1) + g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) id); + if (ret->str[ret->len - 1] == ',') + ret->str[ret->len - 1] = '\0'; + } #else g_string_append (ret, "unknown"); #endif @@ -260,6 +285,9 @@ g_credentials_is_same_user (GCredentials *credentials, #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED if (credentials->native.uid == other_credentials->native.uid) ret = TRUE; +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + if (ucred_geteuid (credentials->native) == ucred_geteuid (other_credentials->native)) + ret = TRUE; #else g_set_error_literal (error, G_IO_ERROR, @@ -334,7 +362,9 @@ g_credentials_get_native (GCredentials *credentials, if (!credentials_native_type_check (native_type, "get")) return NULL; -#if G_CREDENTIALS_SUPPORTED +#if G_CREDENTIALS_USE_SOLARIS_UCRED + return credentials->native; +#elif G_CREDENTIALS_SUPPORTED return &credentials->native; #else g_assert_not_reached (); @@ -364,7 +394,9 @@ g_credentials_set_native (GCredentials *credentials, if (!credentials_native_type_check (native_type, "set")) return; -#if G_CREDENTIALS_SUPPORTED +#if G_CREDENTIALS_USE_SOLARIS_UCRED + memcpy (credentials->native, native, ucred_size ()); +#elif G_CREDENTIALS_SUPPORTED memcpy (&credentials->native, native, sizeof (credentials->native)); #else g_assert_not_reached (); @@ -405,6 +437,8 @@ g_credentials_get_unix_user (GCredentials *credentials, ret = credentials->native.cmcred_euid; #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED ret = credentials->native.uid; +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + ret = ucred_geteuid (credentials->native); #else ret = -1; g_set_error_literal (error, @@ -447,6 +481,8 @@ g_credentials_get_unix_pid (GCredentials *credentials, ret = credentials->native.cmcred_pid; #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED ret = credentials->native.pid; +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + ret = ucred_getpid (credentials->native); #else ret = -1; g_set_error_literal (error, @@ -469,7 +505,8 @@ g_credentials_get_unix_pid (GCredentials *credentials, * * This operation can fail if #GCredentials is not supported on the * OS or if the native credentials type does not contain information - * about the UNIX user. + * about the UNIX user. It can also fail if the OS does not allow the + * use of "spoofed" credentials. * * Returns: %TRUE if @uid was set, %FALSE if error is set. * @@ -496,11 +533,18 @@ g_credentials_set_unix_user (GCredentials *credentials, #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED credentials->native.uid = uid; ret = TRUE; +#elif !G_CREDENTIALS_SPOOFING_SUPPORTED + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + _("Credentials spoofing is not possible on this OS")); + ret = FALSE; #else g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("GCredentials is not implemented on this OS")); + ret = FALSE; #endif return ret; diff --git a/gio/gcredentialsprivate.h b/gio/gcredentialsprivate.h index d3b5f9725..ee7d636e4 100644 --- a/gio/gcredentialsprivate.h +++ b/gio/gcredentialsprivate.h @@ -31,6 +31,7 @@ #define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct ucred)) #define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1 #define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1 +#define G_CREDENTIALS_SPOOFING_SUPPORTED 1 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__GNU__) #define G_CREDENTIALS_SUPPORTED 1 @@ -38,6 +39,7 @@ #define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED #define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct cmsgcred)) #define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1 +#define G_CREDENTIALS_SPOOFING_SUPPORTED 1 #elif defined(__OpenBSD__) #define G_CREDENTIALS_SUPPORTED 1 @@ -45,6 +47,16 @@ #define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED #define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct sockpeercred)) #define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1 +#define G_CREDENTIALS_SPOOFING_SUPPORTED 1 + +#elif defined(__sun__) || defined(__illumos__) || defined (__OpenSolaris_kernel__) +#include +#define G_CREDENTIALS_SUPPORTED 1 +#define G_CREDENTIALS_USE_SOLARIS_UCRED 1 +#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_SOLARIS_UCRED +#define G_CREDENTIALS_NATIVE_SIZE (ucred_size ()) +#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1 +#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1 #endif diff --git a/gio/gioenums.h b/gio/gioenums.h index c8c0e157d..b8b8ec6fb 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1342,6 +1342,7 @@ typedef enum * @G_CREDENTIALS_TYPE_LINUX_UCRED: The native credentials type is a struct ucred. * @G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED: The native credentials type is a struct cmsgcred. * @G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED: The native credentials type is a struct sockpeercred. Added in 2.30. + * @G_CREDENTIALS_TYPE_SOLARIS_UCRED: The native credentials type is a ucred_t. Added in 2.40. * * Enumeration describing different kinds of native credential types. * @@ -1352,7 +1353,8 @@ typedef enum G_CREDENTIALS_TYPE_INVALID, G_CREDENTIALS_TYPE_LINUX_UCRED, G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED, - G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED + G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED, + G_CREDENTIALS_TYPE_SOLARIS_UCRED } GCredentialsType; /** diff --git a/gio/gsocket.c b/gio/gsocket.c index 5e66902fb..714e0b3f2 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -4449,6 +4449,8 @@ g_socket_get_credentials (GSocket *socket, ret = NULL; #if G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED + +#ifdef SO_PEERCRED { guint8 native_creds_buf[G_CREDENTIALS_NATIVE_SIZE]; socklen_t optlen = sizeof (native_creds_buf); @@ -4464,17 +4466,37 @@ g_socket_get_credentials (GSocket *socket, G_CREDENTIALS_NATIVE_TYPE, native_creds_buf); } - else + } +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + { + ucred_t *ucred = NULL; + + if (getpeerucred (socket->priv->fd, &ucred) == 0) { - int errsv = get_socket_errno (); - g_set_error (error, - G_IO_ERROR, - socket_io_error_from_errno (errsv), - _("Unable to read socket credentials: %s"), - socket_strerror (errsv)); + ret = g_credentials_new (); + g_credentials_set_native (ret, + G_CREDENTIALS_TYPE_SOLARIS_UCRED, + ucred); + ucred_free (ucred); } } #else + #error "G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED is set but this is no code for this platform" +#endif + + if (!ret) + { + int errsv = get_socket_errno (); + + g_set_error (error, + G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Unable to read socket credentials: %s"), + socket_strerror (errsv)); + } + +#else + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c index 25432a057..351aea77c 100644 --- a/gio/gunixcredentialsmessage.c +++ b/gio/gunixcredentialsmessage.c @@ -89,6 +89,8 @@ g_unix_credentials_message_get_msg_type (GSocketControlMessage *message) return SCM_CREDENTIALS; #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED return SCM_CREDS; +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + return SCM_UCRED; #elif G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED #error "G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED is set but there is no msg_type defined for this platform" #else diff --git a/gio/tests/credentials.c b/gio/tests/credentials.c index 53a42ecab..5134e388c 100644 --- a/gio/tests/credentials.c +++ b/gio/tests/credentials.c @@ -59,12 +59,22 @@ test_basic (void) g_assert_no_error (error); set = g_credentials_set_unix_user (other, not_me, &error); +#if G_CREDENTIALS_SPOOFING_SUPPORTED g_assert_no_error (error); g_assert (set); g_assert_cmpuint (g_credentials_get_unix_user (other, &error), ==, not_me); g_assert (!g_credentials_is_same_user (creds, other, &error)); g_assert_no_error (error); +#else + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED); + g_assert (!set); + g_clear_error (&error); + + g_assert_cmpuint (g_credentials_get_unix_user (other, &error), ==, geteuid ()); + g_assert (g_credentials_is_same_user (creds, other, &error)); + g_assert_no_error (error); +#endif stringified = g_credentials_to_string (creds); g_test_message ("%s", stringified); @@ -98,6 +108,14 @@ test_basic (void) g_assert_cmpuint (native->uid, ==, geteuid ()); g_assert_cmpuint (native->pid, ==, getpid ()); } +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + { + ucred_t *native = g_credentials_get_native (creds, + G_CREDENTIALS_TYPE_SOLARIS_UCRED); + + g_assert_cmpuint (ucred_geteuid (native), ==, geteuid ()); + g_assert_cmpuint (ucred_getpid (native), ==, getpid ()); + } #else #error "G_CREDENTIALS_SUPPORTED is set but there is no test for this platform" #endif