Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: dbus/object_proxy.cc

Issue 11199007: Add sender verification of D-Bus signals. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: address comments. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "dbus/bus.h" 5 #include "dbus/bus.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/message_loop.h" 9 #include "base/message_loop.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/string_piece.h" 11 #include "base/string_piece.h"
12 #include "base/stringprintf.h" 12 #include "base/stringprintf.h"
13 #include "base/threading/thread.h" 13 #include "base/threading/thread.h"
14 #include "base/threading/thread_restrictions.h" 14 #include "base/threading/thread_restrictions.h"
15 #include "dbus/message.h" 15 #include "dbus/message.h"
16 #include "dbus/object_path.h" 16 #include "dbus/object_path.h"
17 #include "dbus/object_proxy.h" 17 #include "dbus/object_proxy.h"
18 #include "dbus/scoped_dbus_error.h" 18 #include "dbus/scoped_dbus_error.h"
19 19
20 namespace { 20 namespace {
21 21
22 const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown"; 22 const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown";
23 23
24 // Used for success ratio histograms. 1 for success, 0 for failure. 24 // Used for success ratio histograms. 1 for success, 0 for failure.
25 const int kSuccessRatioHistogramMaxValue = 2; 25 const int kSuccessRatioHistogramMaxValue = 2;
26 26
27 // The path of D-Bus Object sending NameOwnerChanged signal.
28 const char kDbusSystemObjectPath[] = "/org/freedesktop/DBus";
29
30 // Timeout in milliseconds used for GetNameOwner method call.
31 const int kGetNameOwnerCallTimeoutMs = 1000;
32
27 // Gets the absolute signal name by concatenating the interface name and 33 // Gets the absolute signal name by concatenating the interface name and
28 // the signal name. Used for building keys for method_table_ in 34 // the signal name. Used for building keys for method_table_ in
29 // ObjectProxy. 35 // ObjectProxy.
30 std::string GetAbsoluteSignalName( 36 std::string GetAbsoluteSignalName(
31 const std::string& interface_name, 37 const std::string& interface_name,
32 const std::string& signal_name) { 38 const std::string& signal_name) {
33 return interface_name + "." + signal_name; 39 return interface_name + "." + signal_name;
34 } 40 }
35 41
36 // An empty function used for ObjectProxy::EmptyResponseCallback(). 42 // An empty function used for ObjectProxy::EmptyResponseCallback().
37 void EmptyResponseCallbackBody(dbus::Response* unused_response) { 43 void EmptyResponseCallbackBody(dbus::Response* unused_response) {
38 } 44 }
39 45
46 // Creates and returns a copy of the pointed Signal instance.
47 dbus::Signal* CloneSignalMessage(dbus::Signal* signal) {
48 DBusMessage* clone = dbus_message_copy(signal->raw_message());
49 return dbus::Signal::FromRawMessage(clone);
50 }
51
40 } // namespace 52 } // namespace
41 53
42 namespace dbus { 54 namespace dbus {
43 55
44 ObjectProxy::ObjectProxy(Bus* bus, 56 ObjectProxy::ObjectProxy(Bus* bus,
45 const std::string& service_name, 57 const std::string& service_name,
46 const ObjectPath& object_path, 58 const ObjectPath& object_path,
47 int options) 59 int options)
48 : bus_(bus), 60 : bus_(bus),
49 service_name_(service_name), 61 service_name_(service_name),
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 filter_added_ = true; 367 filter_added_ = true;
356 } else { 368 } else {
357 LOG(ERROR) << "Failed to add filter function"; 369 LOG(ERROR) << "Failed to add filter function";
358 } 370 }
359 } 371 }
360 // Add a match rule so the signal goes through HandleMessage(). 372 // Add a match rule so the signal goes through HandleMessage().
361 const std::string match_rule = 373 const std::string match_rule =
362 base::StringPrintf("type='signal', interface='%s', path='%s'", 374 base::StringPrintf("type='signal', interface='%s', path='%s'",
363 interface_name.c_str(), 375 interface_name.c_str(),
364 object_path_.value().c_str()); 376 object_path_.value().c_str());
365 377 // Add a match_rule listening NameOwnerChanged for the well-known name
366 // Add the match rule if we don't have it. 378 // |service_name_|.
367 if (match_rules_.find(match_rule) == match_rules_.end()) { 379 const std::string name_owner_changed_match_rule =
368 ScopedDBusError error; 380 base::StringPrintf(
369 bus_->AddMatch(match_rule, error.get());; 381 "type='signal',interface='org.freedesktop.DBus',"
370 if (error.is_set()) { 382 "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
371 LOG(ERROR) << "Failed to add match rule: " << match_rule; 383 "sender='org.freedesktop.DBus',arg0='%s'",
372 } else { 384 service_name_.c_str());
373 // Store the match rule, so that we can remove this in Detach(). 385 if (AddMatchRuleWithCallback(match_rule,
374 match_rules_.insert(match_rule); 386 absolute_signal_name,
375 // Add the signal callback to the method table. 387 signal_callback) &&
376 method_table_[absolute_signal_name] = signal_callback; 388 AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
377 success = true; 389 "org.freedesktop.DBus.NameOwnerChanged")) {
378 }
379 } else {
380 // We already have the match rule.
381 method_table_[absolute_signal_name] = signal_callback;
382 success = true; 390 success = true;
383 } 391 }
392
393 // Try getting the latest name owner. No pending signal.
394 // Passing NULL as the pending signal so that it only call GetNameOwner to
395 // update |service_name_owner_|.
satorux1 2012/10/25 08:56:23 We might want to add: It's not guaranteed that we
Haruki Sato 2012/10/26 05:03:24 Done.
396 UpdateNameOwnerAsync(scoped_ptr<Signal>(NULL));
384 } 397 }
385 398
386 // Run on_connected_callback in the origin thread. 399 // Run on_connected_callback in the origin thread.
387 bus_->PostTaskToOriginThread( 400 bus_->PostTaskToOriginThread(
388 FROM_HERE, 401 FROM_HERE,
389 base::Bind(&ObjectProxy::OnConnected, 402 base::Bind(&ObjectProxy::OnConnected,
390 this, 403 this,
391 on_connected_callback, 404 on_connected_callback,
392 interface_name, 405 interface_name,
393 signal_name, 406 signal_name,
(...skipping 20 matching lines...) Expand all
414 // reference so we can use it in Signal. 427 // reference so we can use it in Signal.
415 dbus_message_ref(raw_message); 428 dbus_message_ref(raw_message);
416 scoped_ptr<Signal> signal( 429 scoped_ptr<Signal> signal(
417 Signal::FromRawMessage(raw_message)); 430 Signal::FromRawMessage(raw_message));
418 431
419 // Verify the signal comes from the object we're proxying for, this is 432 // Verify the signal comes from the object we're proxying for, this is
420 // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and 433 // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
421 // allow other object proxies to handle instead. 434 // allow other object proxies to handle instead.
422 const dbus::ObjectPath path = signal->GetPath(); 435 const dbus::ObjectPath path = signal->GetPath();
423 if (path != object_path_) { 436 if (path != object_path_) {
437 if (path.value() == kDbusSystemObjectPath &&
438 signal->GetMember() == "NameOwnerChanged") {
439 // Handle NameOwnerChanged separately
440 return HandleNameOwnerChanged(signal.get());
441 }
424 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 442 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
425 } 443 }
426 444
445 // If the name owner is not known yet, get it before verifying the sender.
446 if (service_name_owner_.empty()) {
447 return UpdateNameOwnerAsync(signal.Pass());
448 }
satorux1 2012/10/25 08:56:23 I just realized that this would be unnecessary 1)
Haruki Sato 2012/10/26 05:03:24 Discussed offline. I confirmed that There's still
449
450 // Verify the sender, check if we know about the signal, and invoke the
451 // callback if any.
452 return VerifySenderAndDispatch(signal.Pass());
453 }
454
455 DBusHandlerResult ObjectProxy::VerifySenderAndDispatch(
456 scoped_ptr<dbus::Signal> signal) {
457 DCHECK(signal);
458
427 const std::string interface = signal->GetInterface(); 459 const std::string interface = signal->GetInterface();
428 const std::string member = signal->GetMember(); 460 const std::string member = signal->GetMember();
429 461
430 // Check if we know about the signal. 462 // Check if we know about the signal.
431 const std::string absolute_signal_name = GetAbsoluteSignalName( 463 const std::string absolute_signal_name = GetAbsoluteSignalName(
432 interface, member); 464 interface, member);
433 MethodTable::const_iterator iter = method_table_.find(absolute_signal_name); 465 MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
434 if (iter == method_table_.end()) { 466 if (iter == method_table_.end()) {
435 // Don't know about the signal. 467 // Don't know about the signal.
436 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 468 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
437 } 469 }
438 VLOG(1) << "Signal received: " << signal->ToString(); 470 VLOG(1) << "Signal received: " << signal->ToString();
439 471
472 std::string sender = signal->GetSender();
473 if (service_name_owner_ != sender) {
474 LOG(ERROR) << "Rejecting a message from a wrong sender.";
475 UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
476 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
477 }
478
440 const base::TimeTicks start_time = base::TimeTicks::Now(); 479 const base::TimeTicks start_time = base::TimeTicks::Now();
441 if (bus_->HasDBusThread()) { 480 if (bus_->HasDBusThread()) {
442 // Post a task to run the method in the origin thread. 481 // Post a task to run the method in the origin thread.
443 // Transfer the ownership of |signal| to RunMethod(). 482 // Transfer the ownership of |signal| to RunMethod().
444 // |released_signal| will be deleted in RunMethod(). 483 // |released_signal| will be deleted in RunMethod().
445 Signal* released_signal = signal.release(); 484 Signal* released_signal = signal.release();
446 bus_->PostTaskToOriginThread(FROM_HERE, 485 bus_->PostTaskToOriginThread(FROM_HERE,
447 base::Bind(&ObjectProxy::RunMethod, 486 base::Bind(&ObjectProxy::RunMethod,
448 this, 487 this,
449 start_time, 488 start_time,
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
508 std::string error_message; 547 std::string error_message;
509 reader.PopString(&error_message); 548 reader.PopString(&error_message);
510 LogMethodCallFailure(interface_name, 549 LogMethodCallFailure(interface_name,
511 method_name, 550 method_name,
512 error_response->GetErrorName(), 551 error_response->GetErrorName(),
513 error_message); 552 error_message);
514 } 553 }
515 response_callback.Run(NULL); 554 response_callback.Run(NULL);
516 } 555 }
517 556
557 bool ObjectProxy::AddMatchRuleWithCallback(
558 const std::string& match_rule,
559 const std::string& absolute_signal_name,
560 SignalCallback signal_callback) {
561 DCHECK(!match_rule.empty());
562 DCHECK(!absolute_signal_name.empty());
563 bus_->AssertOnDBusThread();
564
565 if (match_rules_.find(match_rule) == match_rules_.end()) {
566 ScopedDBusError error;
567 bus_->AddMatch(match_rule, error.get());
568 if (error.is_set()) {
569 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got " <<
570 error.name() << ": " << error.message();
571 return false;
572 } else {
573 // Store the match rule, so that we can remove this in Detach().
574 match_rules_.insert(match_rule);
575 // Add the signal callback to the method table.
576 method_table_[absolute_signal_name] = signal_callback;
577 return true;
578 }
579 } else {
580 // We already have the match rule.
581 method_table_[absolute_signal_name] = signal_callback;
582 return true;
583 }
584 }
585
586 bool ObjectProxy::AddMatchRuleWithoutCallback(
587 const std::string& match_rule,
588 const std::string& absolute_signal_name) {
589 DCHECK(!match_rule.empty());
590 DCHECK(!absolute_signal_name.empty());
591 bus_->AssertOnDBusThread();
592
593 if (match_rules_.find(match_rule) != match_rules_.end())
594 return true;
595
596 ScopedDBusError error;
597 bus_->AddMatch(match_rule, error.get());
598 if (error.is_set()) {
599 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got " <<
600 error.name() << ": " << error.message();
601 return false;
602 }
603 // Store the match rule, so that we can remove this in Detach().
604 match_rules_.insert(match_rule);
605 return true;
606 }
607
608 DBusHandlerResult ObjectProxy::UpdateNameOwnerAsync(
609 scoped_ptr<Signal> pending_signal) {
610 bus_->AssertOnDBusThread();
611
612 scoped_ptr<MethodCall> get_name_owner_call(
613 new MethodCall("org.freedesktop.DBus", "GetNameOwner"));
614 MessageWriter writer(get_name_owner_call.get());
615 writer.AppendString(service_name_);
616 VLOG(1) << "Method call: " << get_name_owner_call->ToString();
617
618 const dbus::ObjectPath object_path("/org/freedesktop/DBus");
619 ScopedDBusError error;
620 if (!get_name_owner_call->SetDestination("org.freedesktop.DBus") ||
621 !get_name_owner_call->SetPath(object_path)) {
622 LOG(ERROR) << "Failed to get name owner.";
623 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
624 }
625
626 Signal* clone = NULL;
627 if (pending_signal) {
628 // This depends on the incoming reply to GetNameOwner method call.
629 // Copy the pending signal and delete the original in order to free
630 // libdbus's messaging queue and let it accept the next incoming message.
631 // The limitation of the queue size seems to be the one described in
632 // ObjectProxy::RunResponseCallback()
633 clone = CloneSignalMessage(pending_signal.get());
634 pending_signal.reset();
635 }
636 DBusMessage* request_message = get_name_owner_call->raw_message();
637 dbus_message_ref(request_message);
638
639 const base::TimeTicks start_time = base::TimeTicks::Now();
640 StartAsyncMethodCall(
641 kGetNameOwnerCallTimeoutMs,
642 request_message,
643 base::Bind(&ObjectProxy::OnGetNameOwner, this, clone),
644 base::Bind(&ObjectProxy::OnGetNameOwnerError, this, clone),
645 start_time);
646 return DBUS_HANDLER_RESULT_HANDLED;
647 }
648
649 DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(Signal* signal) {
650 DCHECK(signal);
651 bus_->AssertOnDBusThread();
652
653 // Confirm the validity of the NameOwnerChanged signal.
654 if (signal->GetMember() == "NameOwnerChanged" &&
655 signal->GetInterface() == "org.freedesktop.DBus" &&
656 signal->GetSender() == "org.freedesktop.DBus") {
657 MessageReader reader(signal);
658 std::string name, old_owner, new_owner;
659 if (reader.PopString(&name) &&
660 reader.PopString(&old_owner) &&
661 reader.PopString(&new_owner) &&
662 name == service_name_) {
663 service_name_owner_ = new_owner;
664 return DBUS_HANDLER_RESULT_HANDLED;
665 }
666 }
667
668 // Untrusted or uninteresting signal
669 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
670 }
671
672 void ObjectProxy::OnGetNameOwner(Signal* pending_signal, Response* response) {
673 bus_->PostTaskToDBusThread(
674 FROM_HERE,
675 base::Bind(&ObjectProxy::OnGetNameOwnerOnDbusThread,
676 this,
677 pending_signal,
678 response));
679 }
680
681 void ObjectProxy::OnGetNameOwnerOnDbusThread(
682 Signal* pending_signal, Response* response) {
683 bus_->AssertOnDBusThread();
684
685 MessageReader reader(response);
686 if (!reader.PopString(&service_name_owner_)) {
687 LOG(ERROR) << "Can not interpret the result of GetNameOwner: "
688 << response->ToString();
689 }
690
691 if (pending_signal)
692 VerifySenderAndDispatch(scoped_ptr<Signal>(pending_signal));
693 }
694
695 void ObjectProxy::OnGetNameOwnerError(
696 Signal* pending_signal, ErrorResponse* response) {
697 bus_->PostTaskToDBusThread(
698 FROM_HERE,
699 base::Bind(&ObjectProxy::OnGetNameOwnerErrorOnDbusThread,
700 this,
701 pending_signal,
702 response));
703 }
704
705 void ObjectProxy::OnGetNameOwnerErrorOnDbusThread(
706 Signal* pending_signal, ErrorResponse* response) {
707 bus_->AssertOnDBusThread();
708
709 if (pending_signal)
710 VerifySenderAndDispatch(scoped_ptr<Signal>(pending_signal));
711 }
712
518 } // namespace dbus 713 } // namespace dbus
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698