]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
auth: add fd passing negotiation support
authorLennart Poettering <lennart@poettering.net>
Fri, 24 Apr 2009 02:38:27 +0000 (04:38 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 20 May 2009 00:09:31 +0000 (02:09 +0200)
This adds two new directives to the auth protocol:

NEGOTIATE_UNIX_FD is sent by the client after the authentication was
sucessful, i.e. OK was received.

AGREE_UNIX_FD is then sent by the server if it can do unix fd passing as
well.

ERROR is returned when the server cannot or is unwilling to do unix fd
passing.

This should be compatible with existing D-Bus implementations which will
naturally return ERROR on NEGOTIATE_UNIX_FD.

bus/dispatch.c
dbus/dbus-auth.c
dbus/dbus-auth.h
dbus/dbus-transport-protected.h
dbus/dbus-transport-socket.c
dbus/dbus-transport.c

index ca9a843359251e516eb366d6b5d1e60fbd691b2a..8ed88dad41469eefeb23b939f01e66e5a7f051fd 100644 (file)
@@ -4791,6 +4791,11 @@ bus_unix_fds_passing_test(const DBusString *test_data_dir)
   if (!_dbus_close(two[0], &error))
     _dbus_assert_not_reached("Failed to close pipe #2 ");
 
+  if (!(dbus_connection_can_send_type(foo, DBUS_TYPE_UNIX_FD)))
+    _dbus_assert_not_reached("Connection cannot do fd passing");
+
+  if (!(dbus_connection_can_send_type(bar, DBUS_TYPE_UNIX_FD)))
+    _dbus_assert_not_reached("Connection cannot do fd passing");
 
   if (!dbus_connection_send (foo, m, NULL))
     _dbus_assert_not_reached("Failed to send fds");
index ec7cf312b8c77e1c1625c67086e6c481fa5c4cb4..f1c83ae475913727592b63a8637fe8e7f21bc91d 100644 (file)
@@ -122,7 +122,9 @@ typedef enum {
   DBUS_AUTH_COMMAND_REJECTED,
   DBUS_AUTH_COMMAND_OK,
   DBUS_AUTH_COMMAND_ERROR,
-  DBUS_AUTH_COMMAND_UNKNOWN
+  DBUS_AUTH_COMMAND_UNKNOWN,
+  DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD,
+  DBUS_AUTH_COMMAND_AGREE_UNIX_FD
 } DBusAuthCommand;
 
 /**
@@ -184,6 +186,9 @@ struct DBusAuth
   unsigned int already_got_mechanisms : 1;       /**< Client already got mech list */
   unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
   unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
+
+  unsigned int unix_fd_possible : 1;  /**< This side could do unix fd passing */
+  unsigned int unix_fd_negotiated : 1; /**< Unix fd was successfully negotiated */
 };
 
 /**
@@ -223,9 +228,10 @@ static dbus_bool_t send_rejected             (DBusAuth *auth);
 static dbus_bool_t send_error                (DBusAuth *auth,
                                               const char *message);
 static dbus_bool_t send_ok                   (DBusAuth *auth);
-static dbus_bool_t send_begin                (DBusAuth *auth,
-                                              const DBusString *args_from_ok);
+static dbus_bool_t send_begin                (DBusAuth *auth);
 static dbus_bool_t send_cancel               (DBusAuth *auth);
+static dbus_bool_t send_negotiate_unix_fd    (DBusAuth *auth);
+static dbus_bool_t send_agree_unix_fd        (DBusAuth *auth);
 
 /**
  * Client states
@@ -264,6 +270,9 @@ static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *aut
 static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth         *auth,
                                                            DBusAuthCommand   command,
                                                            const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_agree_unix_fd (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
 
 static const DBusAuthStateData client_state_need_send_auth = {
   "NeedSendAuth", NULL
@@ -277,7 +286,10 @@ static const DBusAuthStateData client_state_waiting_for_ok = {
 static const DBusAuthStateData client_state_waiting_for_reject = {
   "WaitingForReject", handle_client_state_waiting_for_reject
 };
-  
+static const DBusAuthStateData client_state_waiting_for_agree_unix_fd = {
+  "WaitingForAgreeUnixFD", handle_client_state_waiting_for_agree_unix_fd
+};
+
 /**
  * Common terminal states.  Terminal states have handler == NULL.
  */
@@ -1522,9 +1534,21 @@ send_ok (DBusAuth *auth)
 }
 
 static dbus_bool_t
-send_begin (DBusAuth         *auth,
-            const DBusString *args_from_ok)
+send_begin (DBusAuth         *auth)
 {
+
+  if (!_dbus_string_append (&auth->outgoing,
+                            "BEGIN\r\n"))
+    return FALSE;
+
+  goto_state (auth, &common_state_authenticated);
+  return TRUE;
+}
+
+static dbus_bool_t
+process_ok(DBusAuth *auth,
+          const DBusString *args_from_ok) {
+
   int end_of_hex;
   
   /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
@@ -1549,20 +1573,19 @@ send_begin (DBusAuth         *auth,
       return TRUE;
     }
 
-  if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) &&
-      _dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
-    {
-      _dbus_verbose ("Got GUID '%s' from the server\n",
-                     _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
-      
-      goto_state (auth, &common_state_authenticated);
-      return TRUE;
-    }
-  else
-    {
+  if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) {
       _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
       return FALSE;
-    }
+  }
+
+  _dbus_verbose ("Got GUID '%s' from the server\n",
+                 _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
+
+  if (auth->unix_fd_possible)
+    return send_negotiate_unix_fd(auth);
+
+  _dbus_verbose("Not negotiating unix fd passing, since not possible\n");
+  return send_begin (auth);
 }
 
 static dbus_bool_t
@@ -1621,6 +1644,33 @@ process_data (DBusAuth             *auth,
   return TRUE;
 }
 
+static dbus_bool_t
+send_negotiate_unix_fd (DBusAuth *auth)
+{
+  if (!_dbus_string_append (&auth->outgoing,
+                            "NEGOTIATE_UNIX_FD\r\n"))
+    return FALSE;
+
+  goto_state (auth, &client_state_waiting_for_agree_unix_fd);
+  return TRUE;
+}
+
+static dbus_bool_t
+send_agree_unix_fd (DBusAuth *auth)
+{
+  _dbus_assert(auth->unix_fd_possible);
+
+  auth->unix_fd_negotiated = TRUE;
+  _dbus_verbose("Agreed to UNIX FD passing\n");
+
+  if (!_dbus_string_append (&auth->outgoing,
+                            "AGREE_UNIX_FD\r\n"))
+    return FALSE;
+
+  goto_state (auth, &server_state_waiting_for_begin);
+  return TRUE;
+}
+
 static dbus_bool_t
 handle_auth (DBusAuth *auth, const DBusString *args)
 {
@@ -1712,9 +1762,13 @@ handle_server_state_waiting_for_auth  (DBusAuth         *auth,
     case DBUS_AUTH_COMMAND_ERROR:
       return send_rejected (auth);
 
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+      return send_error (auth, "Need to authenticate first");
+
     case DBUS_AUTH_COMMAND_REJECTED:
     case DBUS_AUTH_COMMAND_OK:
     case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
     default:
       return send_error (auth, "Unknown command");
     }
@@ -1741,9 +1795,13 @@ handle_server_state_waiting_for_data  (DBusAuth         *auth,
       goto_state (auth, &common_state_need_disconnect);
       return TRUE;
 
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+      return send_error (auth, "Need to authenticate first");
+
     case DBUS_AUTH_COMMAND_REJECTED:
     case DBUS_AUTH_COMMAND_OK:
     case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
     default:
       return send_error (auth, "Unknown command");
     }
@@ -1766,9 +1824,16 @@ handle_server_state_waiting_for_begin (DBusAuth         *auth,
       goto_state (auth, &common_state_authenticated);
       return TRUE;
 
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+      if (auth->unix_fd_possible)
+        return send_agree_unix_fd(auth);
+      else
+        return send_error(auth, "Unix FD passing not supported, not authenticated or otherwise not possible");
+
     case DBUS_AUTH_COMMAND_REJECTED:
     case DBUS_AUTH_COMMAND_OK:
     case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
     default:
       return send_error (auth, "Unknown command");
 
@@ -1933,7 +1998,7 @@ handle_client_state_waiting_for_data (DBusAuth         *auth,
       return process_rejected (auth, args);
 
     case DBUS_AUTH_COMMAND_OK:
-      return send_begin (auth, args);
+      return process_ok(auth, args);
 
     case DBUS_AUTH_COMMAND_ERROR:
       return send_cancel (auth);
@@ -1942,6 +2007,8 @@ handle_client_state_waiting_for_data (DBusAuth         *auth,
     case DBUS_AUTH_COMMAND_CANCEL:
     case DBUS_AUTH_COMMAND_BEGIN:
     case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
     default:
       return send_error (auth, "Unknown command");
     }
@@ -1958,7 +2025,7 @@ handle_client_state_waiting_for_ok (DBusAuth         *auth,
       return process_rejected (auth, args);
 
     case DBUS_AUTH_COMMAND_OK:
-      return send_begin (auth, args);
+      return process_ok(auth, args);
 
     case DBUS_AUTH_COMMAND_DATA:
     case DBUS_AUTH_COMMAND_ERROR:
@@ -1968,6 +2035,8 @@ handle_client_state_waiting_for_ok (DBusAuth         *auth,
     case DBUS_AUTH_COMMAND_CANCEL:
     case DBUS_AUTH_COMMAND_BEGIN:
     case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
     default:
       return send_error (auth, "Unknown command");
     }
@@ -1990,12 +2059,46 @@ handle_client_state_waiting_for_reject (DBusAuth         *auth,
     case DBUS_AUTH_COMMAND_OK:
     case DBUS_AUTH_COMMAND_ERROR:
     case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
     default:
       goto_state (auth, &common_state_need_disconnect);
       return TRUE;
     }
 }
 
+static dbus_bool_t
+handle_client_state_waiting_for_agree_unix_fd(DBusAuth         *auth,
+                                              DBusAuthCommand   command,
+                                              const DBusString *args)
+{
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+      _dbus_assert(auth->unix_fd_possible);
+      auth->unix_fd_negotiated = TRUE;
+      _dbus_verbose("Sucessfully negotiated UNIX FD passing\n");
+      return send_begin (auth);
+
+    case DBUS_AUTH_COMMAND_ERROR:
+      _dbus_assert(auth->unix_fd_possible);
+      auth->unix_fd_negotiated = FALSE;
+      _dbus_verbose("Failed to negotiate UNIX FD passing\n");
+      return send_begin (auth);
+
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_DATA:
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    default:
+      return send_error (auth, "Unknown command");
+    }
+}
+
 /**
  * Mapping from command name to enum
  */
@@ -2005,13 +2108,15 @@ typedef struct {
 } DBusAuthCommandName;
 
 static const DBusAuthCommandName auth_command_names[] = {
-  { "AUTH",     DBUS_AUTH_COMMAND_AUTH },
-  { "CANCEL",   DBUS_AUTH_COMMAND_CANCEL },
-  { "DATA",     DBUS_AUTH_COMMAND_DATA },
-  { "BEGIN",    DBUS_AUTH_COMMAND_BEGIN },
-  { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
-  { "OK",       DBUS_AUTH_COMMAND_OK },
-  { "ERROR",    DBUS_AUTH_COMMAND_ERROR }
+  { "AUTH",              DBUS_AUTH_COMMAND_AUTH },
+  { "CANCEL",            DBUS_AUTH_COMMAND_CANCEL },
+  { "DATA",              DBUS_AUTH_COMMAND_DATA },
+  { "BEGIN",             DBUS_AUTH_COMMAND_BEGIN },
+  { "REJECTED",          DBUS_AUTH_COMMAND_REJECTED },
+  { "OK",                DBUS_AUTH_COMMAND_OK },
+  { "ERROR",             DBUS_AUTH_COMMAND_ERROR },
+  { "NEGOTIATE_UNIX_FD", DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD },
+  { "AGREE_UNIX_FD",     DBUS_AUTH_COMMAND_AGREE_UNIX_FD }
 };
 
 static DBusAuthCommand
@@ -2685,6 +2790,31 @@ _dbus_auth_set_context (DBusAuth               *auth,
                                    &auth->context, 0, _dbus_string_get_length (context));
 }
 
+/**
+ * Sets whether unix fd passing is potentially on the transport and
+ * hence shall be negotiated.
+ *
+ * @param auth the auth conversation
+ * @param b TRUE when unix fd passing shall be negotiated, otherwise FALSE
+ */
+void
+_dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b)
+{
+  auth->unix_fd_possible = b;
+}
+
+/**
+ * Queries whether unix fd passing was sucessfully negotiated.
+ *
+ * @param auth the auth conversion
+ * @returns #TRUE when unix fd passing was negotiated.
+ */
+dbus_bool_t
+_dbus_auth_get_unix_fd_negotiated(DBusAuth *auth)
+{
+  return auth->unix_fd_negotiated;
+}
+
 /** @} */
 
 /* tests in dbus-auth-util.c */
index 14f8320a7fab8a5a6300d60f599eb7be3c649452..5680df118f15789600e85487f6727672ce454ba7 100644 (file)
@@ -75,6 +75,8 @@ dbus_bool_t   _dbus_auth_set_context         (DBusAuth               *auth,
                                               const DBusString       *context);
 const char*   _dbus_auth_get_guid_from_server(DBusAuth               *auth);
 
+void          _dbus_auth_set_unix_fd_possible(DBusAuth               *auth, dbus_bool_t b);
+dbus_bool_t   _dbus_auth_get_unix_fd_negotiated(DBusAuth             *auth);
 
 DBUS_END_DECLS
 
index 023549d0544233c0e1e03118ec1ff334890bb141..8c389a6dbc73d13d673907c360881061fe23313a 100644 (file)
@@ -144,6 +144,9 @@ DBusTransportOpenResult _dbus_transport_open_platform_specific (DBusAddressEntry
                                                                 DBusTransport    **transport_p,
                                                                 DBusError         *error);
 
+#define DBUS_TRANSPORT_CAN_SEND_UNIX_FD(x)      \
+  _dbus_auth_get_unix_fd_negotiated((x)->auth)
+
 DBUS_END_DECLS
 
 #endif /* DBUS_TRANSPORT_PROTECTED_H */
index cc49c85121a946b67ca33be0fc72caf35d97537b..766995061ba65833578f9614ac60a490b307a34a 100644 (file)
@@ -594,7 +594,7 @@ do_writing (DBusTransport *transport)
 #endif
 
 #ifdef HAVE_UNIX_FD_PASSING
-          if (socket_transport->message_bytes_written <= 0 && transport->can_pass_unix_fd)
+          if (socket_transport->message_bytes_written <= 0 && DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport))
             {
               /* Send the fds along with the first byte of the message */
               const int *unix_fds;
@@ -777,7 +777,7 @@ do_reading (DBusTransport *transport)
                                        &buffer);
 
 #ifdef HAVE_UNIX_FD_PASSING
-      if (transport->can_pass_unix_fd)
+      if (DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport))
         {
           int *fds, n_fds;
 
@@ -1244,7 +1244,7 @@ _dbus_transport_new_for_socket (int               fd,
     goto failed_4;
 
 #ifdef HAVE_UNIX_FD_PASSING
-  socket_transport->base.can_pass_unix_fd = _dbus_socket_can_pass_unix_fd(fd);
+  _dbus_auth_set_unix_fd_possible(socket_transport->base.auth, _dbus_socket_can_pass_unix_fd(fd));
 #endif
 
   socket_transport->fd = fd;
index 97ee0e9b0304e47351915dabfe0d19c6b065b495..7ba01357ef21e9af3cc888814bfed2a05b40004d 100644 (file)
@@ -184,7 +184,6 @@ _dbus_transport_init_base (DBusTransport             *transport,
   transport->credentials = creds;
 
 #ifdef HAVE_UNIX_FD_PASSING
-  transport->can_pass_unix_fd = FALSE;
   transport->unix_fds = NULL;
   transport->n_unix_fds = 0;
 #endif