mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 00:12:19 +01:00 
			
		
		
		
	gsocket: Fix credentials error-handling on Apple OSes
- When querying a TCP socket, getsockopt() may succeed but the resulting `optlen` will be zero. This means we'd previously be reading uninitialized stack memory in such cases. - After a file-descriptor has gone through FD-passing, getsockopt() may fail with EINVAL. At least this is the case with TCP sockets. - While at it also use SOL_LOCAL instead of hard-coding its value.
This commit is contained in:
		
				
					committed by
					
						 Philip Withnall
						Philip Withnall
					
				
			
			
				
	
			
			
			
						parent
						
							9a7ca661a3
						
					
				
				
					commit
					9ac3a27f03
				
			| @@ -5965,10 +5965,11 @@ g_socket_get_credentials (GSocket   *socket, | ||||
|     socklen_t optlen = sizeof (cred); | ||||
|  | ||||
|     if (getsockopt (socket->priv->fd, | ||||
|                     0, | ||||
|                     SOL_LOCAL, | ||||
|                     LOCAL_PEERCRED, | ||||
|                     &cred, | ||||
|                     &optlen) == 0) | ||||
|                     &optlen) == 0 | ||||
|         && optlen != 0) | ||||
|       { | ||||
|         if (cred.cr_version == XUCRED_VERSION) | ||||
|           { | ||||
| @@ -5993,6 +5994,15 @@ g_socket_get_credentials (GSocket   *socket, | ||||
|             return NULL; | ||||
|           } | ||||
|       } | ||||
|     else if (optlen == 0 || errno == EINVAL) | ||||
|       { | ||||
|         g_set_error (error, | ||||
|                      G_IO_ERROR, | ||||
|                      G_IO_ERROR_NOT_SUPPORTED, | ||||
|                      _("Unable to read socket credentials: %s"), | ||||
|                      "unsupported socket type"); | ||||
|         return NULL; | ||||
|       } | ||||
|   } | ||||
| #elif G_CREDENTIALS_USE_NETBSD_UNPCBID | ||||
|   { | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
|  | ||||
| #include <gio/gio.h> | ||||
|  | ||||
| #include <gio/gcredentialsprivate.h> | ||||
| #ifdef G_OS_UNIX | ||||
| #include <errno.h> | ||||
| #include <sys/wait.h> | ||||
| @@ -1898,6 +1899,216 @@ test_read_write (gconstpointer user_data) | ||||
|   g_object_unref (client); | ||||
| } | ||||
|  | ||||
| #if G_CREDENTIALS_SUPPORTED | ||||
| static gpointer client_setup_thread (gpointer user_data); | ||||
|  | ||||
| static void | ||||
| test_credentials_tcp_client (void) | ||||
| { | ||||
|   const GSocketFamily family = G_SOCKET_FAMILY_IPV4; | ||||
|   IPTestData *data; | ||||
|   GError *error = NULL; | ||||
|   GSocket *client; | ||||
|   GSocketAddress *addr; | ||||
|   GCredentials *creds; | ||||
|  | ||||
|   data = create_server (family, echo_server_thread, FALSE, &error); | ||||
|   if (error != NULL) | ||||
|     { | ||||
|       gchar *message = g_strdup_printf ("Failed to create server: %s", error->message); | ||||
|       g_test_skip (message); | ||||
|       g_free (message); | ||||
|       g_clear_error (&error); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   addr = g_socket_get_local_address (data->server, &error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   client = g_socket_new (family, | ||||
| 			 G_SOCKET_TYPE_STREAM, | ||||
| 			 G_SOCKET_PROTOCOL_DEFAULT, | ||||
| 			 &error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   g_socket_set_blocking (client, TRUE); | ||||
|   g_socket_set_timeout (client, 1); | ||||
|  | ||||
|   g_socket_connect (client, addr, NULL, &error); | ||||
|   g_assert_no_error (error); | ||||
|   g_object_unref (addr); | ||||
|  | ||||
|   creds = g_socket_get_credentials (client, &error); | ||||
|   if (creds != NULL) | ||||
|     { | ||||
|       gchar *str = g_credentials_to_string (creds); | ||||
|       g_print ("Supported on this OS: %s\n", str); | ||||
|       g_free (str); | ||||
|       g_clear_object (&creds); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); | ||||
|       g_print ("Unsupported on this OS: %s\n", error->message); | ||||
|       g_clear_error (&error); | ||||
|     } | ||||
|  | ||||
|   g_socket_close (client, &error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   g_thread_join (data->thread); | ||||
|  | ||||
|   g_socket_close (data->server, &error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   g_object_unref (data->server); | ||||
|   g_object_unref (client); | ||||
|  | ||||
|   g_slice_free (IPTestData, data); | ||||
| } | ||||
|  | ||||
| static void | ||||
| test_credentials_tcp_server (void) | ||||
| { | ||||
|   const GSocketFamily family = G_SOCKET_FAMILY_IPV4; | ||||
|   IPTestData *data; | ||||
|   GSocket *server; | ||||
|   GError *error = NULL; | ||||
|   GSocketAddress *addr = NULL; | ||||
|   GInetAddress *iaddr = NULL; | ||||
|   GSocket *sock = NULL; | ||||
|   GCredentials *creds; | ||||
|  | ||||
|   data = g_slice_new0 (IPTestData); | ||||
|   data->family = family; | ||||
|   data->server = server = g_socket_new (family, | ||||
| 					G_SOCKET_TYPE_STREAM, | ||||
| 					G_SOCKET_PROTOCOL_DEFAULT, | ||||
| 					&error); | ||||
|   if (error != NULL) | ||||
|     goto skip; | ||||
|  | ||||
|   g_socket_set_blocking (server, TRUE); | ||||
|  | ||||
|   iaddr = g_inet_address_new_loopback (family); | ||||
|   addr = g_inet_socket_address_new (iaddr, 0); | ||||
|  | ||||
|   if (!g_socket_bind (server, addr, TRUE, &error)) | ||||
|     goto skip; | ||||
|  | ||||
|   if (!g_socket_listen (server, &error)) | ||||
|     goto skip; | ||||
|  | ||||
|   data->thread = g_thread_new ("client", client_setup_thread, data); | ||||
|  | ||||
|   sock = g_socket_accept (server, NULL, &error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   creds = g_socket_get_credentials (sock, &error); | ||||
|   if (creds != NULL) | ||||
|     { | ||||
|       gchar *str = g_credentials_to_string (creds); | ||||
|       g_print ("Supported on this OS: %s\n", str); | ||||
|       g_free (str); | ||||
|       g_clear_object (&creds); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); | ||||
|       g_print ("Unsupported on this OS: %s\n", error->message); | ||||
|       g_clear_error (&error); | ||||
|     } | ||||
|  | ||||
|   goto beach; | ||||
|  | ||||
| skip: | ||||
|   { | ||||
|     gchar *message = g_strdup_printf ("Failed to create server: %s", error->message); | ||||
|     g_test_skip (message); | ||||
|     g_free (message); | ||||
|  | ||||
|     goto beach; | ||||
|   } | ||||
| beach: | ||||
|   { | ||||
|     g_clear_error (&error); | ||||
|  | ||||
|     g_clear_object (&sock); | ||||
|     g_clear_object (&addr); | ||||
|     g_clear_object (&iaddr); | ||||
|  | ||||
|     g_clear_pointer (&data->thread, g_thread_join); | ||||
|     g_clear_object (&data->server); | ||||
|     g_clear_object (&data->client); | ||||
|  | ||||
|     g_slice_free (IPTestData, data); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static gpointer | ||||
| client_setup_thread (gpointer user_data) | ||||
| { | ||||
|   IPTestData *data = user_data; | ||||
|   GSocketAddress *addr; | ||||
|   GSocket *client; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   addr = g_socket_get_local_address (data->server, &error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   data->client = client = g_socket_new (data->family, | ||||
| 					G_SOCKET_TYPE_STREAM, | ||||
| 					G_SOCKET_PROTOCOL_DEFAULT, | ||||
| 					&error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   g_socket_set_blocking (client, TRUE); | ||||
|   g_socket_set_timeout (client, 1); | ||||
|  | ||||
|   g_socket_connect (client, addr, NULL, &error); | ||||
|   g_assert_no_error (error); | ||||
|  | ||||
|   g_object_unref (addr); | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| #ifdef G_OS_UNIX | ||||
| static void | ||||
| test_credentials_unix_socketpair (void) | ||||
| { | ||||
|   gint fds[2]; | ||||
|   gint status; | ||||
|   GSocket *sock; | ||||
|   GError *error = NULL; | ||||
|   GCredentials *creds; | ||||
|  | ||||
|   status = socketpair (PF_UNIX, SOCK_STREAM, 0, fds); | ||||
|   g_assert_cmpint (status, ==, 0); | ||||
|  | ||||
|   sock = g_socket_new_from_fd (fds[0], &error); | ||||
|  | ||||
|   creds = g_socket_get_credentials (sock, &error); | ||||
|   if (creds != NULL) | ||||
|     { | ||||
|       gchar *str = g_credentials_to_string (creds); | ||||
|       g_print ("Supported on this OS: %s\n", str); | ||||
|       g_free (str); | ||||
|       g_clear_object (&creds); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); | ||||
|       g_print ("Unsupported on this OS: %s\n", error->message); | ||||
|       g_clear_error (&error); | ||||
|     } | ||||
|  | ||||
|   g_object_unref (sock); | ||||
|   close (fds[1]); | ||||
| } | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| int | ||||
| main (int   argc, | ||||
|       char *argv[]) | ||||
| @@ -1954,6 +2165,13 @@ main (int   argc, | ||||
|                         test_read_write); | ||||
|   g_test_add_data_func ("/socket/read_writev", GUINT_TO_POINTER (TRUE), | ||||
|                         test_read_write); | ||||
| #if G_CREDENTIALS_SUPPORTED | ||||
|   g_test_add_func ("/socket/credentials/tcp_client", test_credentials_tcp_client); | ||||
|   g_test_add_func ("/socket/credentials/tcp_server", test_credentials_tcp_server); | ||||
| #ifdef G_OS_UNIX | ||||
|   g_test_add_func ("/socket/credentials/unix_socketpair", test_credentials_unix_socketpair); | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
|   return g_test_run(); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user