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 ca9a843..8ed88da 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 ec7cf31..f1c83ae 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
@@ -1622,6 +1645,33 @@ process_data (DBusAuth             *auth,
 }
 
 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)
 {
   if (_dbus_string_get_length (args) == 0)
@@ -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 14f8320..5680df1 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 023549d..8c389a6 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 cc49c85..7669950 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 97ee0e9..7ba0135 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