Fix passing QDBusUnixFileDescriptors over peer-to-peer connections

Passing QDBusUnixFileDescriptors over peer-to-peer connections currently
does not work, because QDBusConnectionPrivate::setPeer() does not set
this->capabilities (unlike QDBusConnectionPrivate::setConnection()
which is executed for bus connections).

Keep track of whether the connection already has been authenticated, and
once the connection does get authenticated, update this->capabilities.

Note that sending a message directly after connecting (before any
message has been received from the peer) might still fail if the message
contains a QDBusUnixFileDescriptor.

Pick-to: 5.15
Fixes: QTBUG-85396
Change-Id: Ib83213ebcd3255fb091c6faefb3618745b8d736c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Steffen Kieß 2020-07-13 18:40:12 +02:00
parent 2a5ad75ec8
commit 6d880185b1
6 changed files with 44 additions and 14 deletions

View File

@ -213,6 +213,8 @@ DEFINEFUNC(DBusDispatchStatus , dbus_connection_dispatch, (DBusConnection
(connection), return)
DEFINEFUNC(DBusDispatchStatus , dbus_connection_get_dispatch_status, (DBusConnection *connection),
(connection), return)
DEFINEFUNC(dbus_bool_t, dbus_connection_get_is_authenticated, (DBusConnection * connection),
(connection), return )
DEFINEFUNC(dbus_bool_t , dbus_connection_get_is_connected, (DBusConnection *connection),
(connection), return)
DEFINEFUNC(DBusConnection* , dbus_connection_open_private, (const char *address,

View File

@ -1119,7 +1119,7 @@ QString QDBusConnection::name() const
*/
QDBusConnection::ConnectionCapabilities QDBusConnection::connectionCapabilities() const
{
return d ? d->capabilities : ConnectionCapabilities();
return d ? d->connectionCapabilities() : ConnectionCapabilities();
}
/*!

View File

@ -277,6 +277,8 @@ private:
void _q_newConnection(QDBusConnectionPrivate *newConnection);
void handleAuthentication();
protected:
void timerEvent(QTimerEvent *e) override;
@ -309,7 +311,11 @@ signals:
public:
QAtomicInt ref;
QDBusConnection::ConnectionCapabilities capabilities;
QAtomicInt capabilities;
QDBusConnection::ConnectionCapabilities connectionCapabilities() const
{
return (QDBusConnection::ConnectionCapabilities)capabilities.loadRelaxed();
}
QString name; // this connection's name
QString baseService; // this connection's base service
QStringList serverConnectionNames;
@ -342,6 +348,7 @@ public:
bool anonymousAuthenticationAllowed;
bool dispatchEnabled; // protected by the dispatch lock, not the main lock
bool isAuthenticated;
public:
// static methods

View File

@ -526,7 +526,7 @@ qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
if (d->mode == QDBusConnectionPrivate::InvalidMode)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->capabilities);
QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->connectionCapabilities());
qDBusDebug() << d << "got message (signal):" << amsg;
return d->handleMessage(amsg) ?
@ -1028,11 +1028,15 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q
extern bool qDBusInitThreads();
QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p)
: QObject(p), ref(1), mode(InvalidMode), busService(nullptr),
: QObject(p),
ref(1),
mode(InvalidMode),
busService(nullptr),
connection(nullptr),
rootNode(QString(QLatin1Char('/'))),
anonymousAuthenticationAllowed(false),
dispatchEnabled(true)
dispatchEnabled(true),
isAuthenticated(false)
{
static const bool threads = q_dbus_threads_init_default();
if (::isDebugging == -1)
@ -1218,6 +1222,9 @@ void QDBusConnectionPrivate::socketRead(qintptr fd)
}
++it;
}
if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
&& q_dbus_connection_get_is_authenticated(connection))
handleAuthentication();
doDispatch();
}
@ -1232,6 +1239,9 @@ void QDBusConnectionPrivate::socketWrite(qintptr fd)
}
++it;
}
if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
&& q_dbus_connection_get_is_authenticated(connection))
handleAuthentication();
}
void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
@ -1274,7 +1284,8 @@ void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, in
QDBusMessagePrivate::setParametersValidated(message, true);
message.setArguments(args);
QDBusError error;
DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error);
DBusMessage *msg =
QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
if (!msg) {
qWarning("QDBusConnection: Could not emit signal %s.%s: %s", qPrintable(interface), memberName.constData(),
qPrintable(error.message()));
@ -1794,6 +1805,12 @@ static QDBusConnection::ConnectionCapabilities connectionCapabilies(DBusConnecti
return result;
}
void QDBusConnectionPrivate::handleAuthentication()
{
capabilities.storeRelaxed(connectionCapabilies(connection));
isAuthenticated = true;
}
void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusErrorInternal &error)
{
mode = ClientMode;
@ -1807,7 +1824,8 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusError
const char *service = q_dbus_bus_get_unique_name(connection);
Q_ASSERT(service);
baseService = QString::fromUtf8(service);
capabilities = connectionCapabilies(connection);
// bus connections are already authenticated here because q_dbus_bus_register() has been called
handleAuthentication();
q_dbus_connection_set_exit_on_disconnect(connection, false);
q_dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
@ -1876,7 +1894,7 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
if (q_dbus_pending_call_get_completed(call->pending)) {
// decode the message
DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending);
msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities);
msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->connectionCapabilities());
q_dbus_message_unref(reply);
} else {
msg = QDBusMessage::createError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage());
@ -1930,7 +1948,8 @@ bool QDBusConnectionPrivate::send(const QDBusMessage& message)
// through the d_ptr->localReply link
QDBusError error;
DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error);
DBusMessage *msg =
QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
if (!msg) {
if (message.type() == QDBusMessage::MethodCallMessage)
qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
@ -2144,7 +2163,8 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM
}
QDBusError error;
DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error);
DBusMessage *msg =
QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
if (!msg) {
qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
qPrintable(message.service()), qPrintable(message.path()),

View File

@ -282,7 +282,7 @@ QDBusMessage QDBusMessagePrivate::makeLocal(const QDBusConnectionPrivate &conn,
// we must marshall and demarshall again so as to create QDBusArgument
// entries for the complex types
QDBusError error;
DBusMessage *message = toDBusMessage(asSent, conn.capabilities, &error);
DBusMessage *message = toDBusMessage(asSent, conn.connectionCapabilities(), &error);
if (!message) {
// failed to marshall, so it's a call error
return QDBusMessage::createError(error);
@ -290,7 +290,7 @@ QDBusMessage QDBusMessagePrivate::makeLocal(const QDBusConnectionPrivate &conn,
q_dbus_message_set_sender(message, conn.baseService.toUtf8());
QDBusMessage retval = fromDBusMessage(message, conn.capabilities);
QDBusMessage retval = fromDBusMessage(message, conn.connectionCapabilities());
retval.d_ptr->localMessage = true;
q_dbus_message_unref(message);
if (retval.d_ptr->service.isEmpty())

View File

@ -1110,8 +1110,9 @@ void tst_QDBusMarshall::receiveUnknownType()
QSKIP("Your session bus does not allow sending Unix file descriptors");
// make sure this QDBusConnection won't handle Unix file descriptors
QDBusConnection::ConnectionCapabilities &capabRef = QDBusConnectionPrivate::d(con)->capabilities;
SetResetValue<QDBusConnection::ConnectionCapabilities> resetter(capabRef, capabRef & ~QDBusConnection::UnixFileDescriptorPassing);
QAtomicInt &capabRef = QDBusConnectionPrivate::d(con)->capabilities;
SetResetValue<QAtomicInt> resetter(capabRef,
capabRef & ~QDBusConnection::UnixFileDescriptorPassing);
if (qstrcmp(QTest::currentDataTag(), "in-call") == 0) {
// create a call back to us containing a file descriptor