Skip to content

Commit

Permalink
Add sender verification of D-Bus signals.
Browse files Browse the repository at this point in the history
The CL does the following:
- Add a match rule for NameOwnerChanged signal from org.freedesktop.DBus.
- Update the owner of the well-known bus name when a NameOwnerChanged comes.
- Call GetNameOwner method to update the latest if ObjectProxy instance does not know the owner.
- Verify the sender of the signal and reject the unknown senders.
- Add UMA_HISTOGRAM_COUNTS "DBus.RejectedSignalCount" for rejected signals.

and a unittest.

BUG=140938
TEST=manual, unittests

Review URL: https://chromiumcodereview.appspot.com/11199007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@164597 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
haruki@chromium.org committed Oct 29, 2012
1 parent 981f063 commit 8bbe31e
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 17 deletions.
1 change: 1 addition & 0 deletions dbus/dbus.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
'message_unittest.cc',
'mock_unittest.cc',
'property_unittest.cc',
'signal_sender_verification_unittest.cc',
'string_util_unittest.cc',
'test_service.cc',
'test_service.h',
Expand Down
161 changes: 144 additions & 17 deletions dbus/object_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown";
// Used for success ratio histograms. 1 for success, 0 for failure.
const int kSuccessRatioHistogramMaxValue = 2;

// The path of D-Bus Object sending NameOwnerChanged signal.
const char kDbusSystemObjectPath[] = "/org/freedesktop/DBus";

// Gets the absolute signal name by concatenating the interface name and
// the signal name. Used for building keys for method_table_ in
// ObjectProxy.
Expand Down Expand Up @@ -362,25 +365,27 @@ void ObjectProxy::ConnectToSignalInternal(
base::StringPrintf("type='signal', interface='%s', path='%s'",
interface_name.c_str(),
object_path_.value().c_str());

// Add the match rule if we don't have it.
if (match_rules_.find(match_rule) == match_rules_.end()) {
ScopedDBusError error;
bus_->AddMatch(match_rule, error.get());;
if (error.is_set()) {
LOG(ERROR) << "Failed to add match rule: " << match_rule;
} else {
// Store the match rule, so that we can remove this in Detach().
match_rules_.insert(match_rule);
// Add the signal callback to the method table.
method_table_[absolute_signal_name] = signal_callback;
success = true;
}
} else {
// We already have the match rule.
method_table_[absolute_signal_name] = signal_callback;
// Add a match_rule listening NameOwnerChanged for the well-known name
// |service_name_|.
const std::string name_owner_changed_match_rule =
base::StringPrintf(
"type='signal',interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',path='/org/freedesktop/DBus',"
"sender='org.freedesktop.DBus',arg0='%s'",
service_name_.c_str());
if (AddMatchRuleWithCallback(match_rule,
absolute_signal_name,
signal_callback) &&
AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
"org.freedesktop.DBus.NameOwnerChanged")) {
success = true;
}

// Try getting the current name owner. It's not guaranteed that we can get
// the name owner at this moment, as the service may not yet be started. If
// that's the case, we'll get the name owner via NameOwnerChanged signal,
// as soon as the service is started.
UpdateNameOwnerAndBlock();
}

// Run on_connected_callback in the origin thread.
Expand All @@ -407,6 +412,7 @@ DBusHandlerResult ObjectProxy::HandleMessage(
DBusConnection* connection,
DBusMessage* raw_message) {
bus_->AssertOnDBusThread();

if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

Expand All @@ -421,6 +427,11 @@ DBusHandlerResult ObjectProxy::HandleMessage(
// allow other object proxies to handle instead.
const dbus::ObjectPath path = signal->GetPath();
if (path != object_path_) {
if (path.value() == kDbusSystemObjectPath &&
signal->GetMember() == "NameOwnerChanged") {
// Handle NameOwnerChanged separately
return HandleNameOwnerChanged(signal.get());
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

Expand All @@ -437,6 +448,13 @@ DBusHandlerResult ObjectProxy::HandleMessage(
}
VLOG(1) << "Signal received: " << signal->ToString();

std::string sender = signal->GetSender();
if (service_name_owner_ != sender) {
LOG(ERROR) << "Rejecting a message from a wrong sender.";
UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

const base::TimeTicks start_time = base::TimeTicks::Now();
if (bus_->HasDBusThread()) {
// Post a task to run the method in the origin thread.
Expand Down Expand Up @@ -515,4 +533,113 @@ void ObjectProxy::OnCallMethodError(const std::string& interface_name,
response_callback.Run(NULL);
}

bool ObjectProxy::AddMatchRuleWithCallback(
const std::string& match_rule,
const std::string& absolute_signal_name,
SignalCallback signal_callback) {
DCHECK(!match_rule.empty());
DCHECK(!absolute_signal_name.empty());
bus_->AssertOnDBusThread();

if (match_rules_.find(match_rule) == match_rules_.end()) {
ScopedDBusError error;
bus_->AddMatch(match_rule, error.get());
if (error.is_set()) {
LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got " <<
error.name() << ": " << error.message();
return false;
} else {
// Store the match rule, so that we can remove this in Detach().
match_rules_.insert(match_rule);
// Add the signal callback to the method table.
method_table_[absolute_signal_name] = signal_callback;
return true;
}
} else {
// We already have the match rule.
method_table_[absolute_signal_name] = signal_callback;
return true;
}
}

bool ObjectProxy::AddMatchRuleWithoutCallback(
const std::string& match_rule,
const std::string& absolute_signal_name) {
DCHECK(!match_rule.empty());
DCHECK(!absolute_signal_name.empty());
bus_->AssertOnDBusThread();

if (match_rules_.find(match_rule) != match_rules_.end())
return true;

ScopedDBusError error;
bus_->AddMatch(match_rule, error.get());
if (error.is_set()) {
LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got " <<
error.name() << ": " << error.message();
return false;
}
// Store the match rule, so that we can remove this in Detach().
match_rules_.insert(match_rule);
return true;
}

void ObjectProxy::UpdateNameOwnerAndBlock() {
bus_->AssertOnDBusThread();

MethodCall get_name_owner_call("org.freedesktop.DBus", "GetNameOwner");
MessageWriter writer(&get_name_owner_call);
writer.AppendString(service_name_);
VLOG(1) << "Method call: " << get_name_owner_call.ToString();

const dbus::ObjectPath obj_path("/org/freedesktop/DBus");
ScopedDBusError error;
if (!get_name_owner_call.SetDestination("org.freedesktop.DBus") ||
!get_name_owner_call.SetPath(obj_path)) {
LOG(ERROR) << "Failed to get name owner.";
return;
}

DBusMessage* response_message = bus_->SendWithReplyAndBlock(
get_name_owner_call.raw_message(),
TIMEOUT_USE_DEFAULT,
error.get());
if (!response_message) {
LOG(ERROR) << "Failed to get name owner. Got " << error.name() << ": " <<
error.message();
return;
}
scoped_ptr<Response> response(Response::FromRawMessage(response_message));
MessageReader reader(response.get());

std::string new_service_name_owner;
if (reader.PopString(&new_service_name_owner))
service_name_owner_ = new_service_name_owner;
else
service_name_owner_.clear();
}

DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(Signal* signal) {
DCHECK(signal);
bus_->AssertOnDBusThread();

// Confirm the validity of the NameOwnerChanged signal.
if (signal->GetMember() == "NameOwnerChanged" &&
signal->GetInterface() == "org.freedesktop.DBus" &&
signal->GetSender() == "org.freedesktop.DBus") {
MessageReader reader(signal);
std::string name, old_owner, new_owner;
if (reader.PopString(&name) &&
reader.PopString(&old_owner) &&
reader.PopString(&new_owner) &&
name == service_name_) {
service_name_owner_ = new_owner;
return DBUS_HANDLER_RESULT_HANDLED;
}
}

// Untrusted or uninteresting signal
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

} // namespace dbus
21 changes: 21 additions & 0 deletions dbus/object_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,24 @@ class ObjectProxy : public base::RefCountedThreadSafe<ObjectProxy> {
ResponseCallback response_callback,
ErrorResponse* error_response);

// Adds the match rule to the bus and associate the callback with the signal.
bool AddMatchRuleWithCallback(const std::string& match_rule,
const std::string& absolute_signal_name,
SignalCallback signal_callback);

// Adds the match rule to the bus so that HandleMessage can see the signal.
bool AddMatchRuleWithoutCallback(const std::string& match_rule,
const std::string& absolute_signal_name);

// Calls D-Bus's GetNameOwner method synchronously to update
// |service_name_owner_| with the current owner of |service_name_|.
//
// BLOCKING CALL.
void UpdateNameOwnerAndBlock();

// Handles NameOwnerChanged signal from D-Bus's special message bus.
DBusHandlerResult HandleNameOwnerChanged(dbus::Signal* signal);

scoped_refptr<Bus> bus_;
std::string service_name_;
ObjectPath object_path_;
Expand All @@ -252,6 +270,9 @@ class ObjectProxy : public base::RefCountedThreadSafe<ObjectProxy> {

const bool ignore_service_unknown_errors_;

// Known name owner of the well-known bus name represnted by |service_name_|.
std::string service_name_owner_;

DISALLOW_COPY_AND_ASSIGN(ObjectProxy);
};

Expand Down
Loading

0 comments on commit 8bbe31e

Please sign in to comment.