Project import
diff --git a/firewalld/Android.mk b/firewalld/Android.mk new file mode 100644 index 0000000..d0f44f4 --- /dev/null +++ b/firewalld/Android.mk
@@ -0,0 +1,100 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + + +# Definitions applying to all targets. Be sure to $(eval) this last. +define firewalld_common_cpp + LOCAL_CPP_EXTENSION := .cc + LOCAL_CLANG := true + LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter + LOCAL_CPPFLAGS += -Wno-sign-promo +endef + +# Definitions applying to all targets except the client library. +# Be sure to $(eval) this last. +define firewalld_common + LOCAL_SHARED_LIBRARIES += \ + libbinder \ + libbinderwrapper \ + libbrillo \ + libbrillo-binder \ + libbrillo-dbus \ + libbrillo-minijail \ + libchrome \ + libchrome-dbus \ + libdbus \ + libminijail \ + libutils + $(eval $(firewalld_common_cpp)) +endef + +# === libfirewalld-client (shared library) === +include $(CLEAR_VARS) +LOCAL_MODULE := libfirewalld-client +LOCAL_SRC_FILES := \ + dbus_bindings/dbus-service-config.json \ + dbus_bindings/org.chromium.Firewalld.dbus-xml +LOCAL_DBUS_PROXY_PREFIX := firewalld +include $(BUILD_SHARED_LIBRARY) + +# === libfirewalld-binder-client (shared library) === +include $(CLEAR_VARS) +LOCAL_MODULE := libfirewalld-binder-client +LOCAL_SRC_FILES := \ + binder_bindings/android/firewalld/IFirewall.aidl \ + firewall_binder_client.cc +LOCAL_SHARED_LIBRARIES := libutils libbinder libchrome +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_CFLAGS += -I$(LOCAL_PATH)/include +$(eval $(firewalld_common_cpp)) +include $(BUILD_SHARED_LIBRARY) + +# === libfirewalld (static library) === +include $(CLEAR_VARS) +LOCAL_MODULE := libfirewalld +LOCAL_SRC_FILES := \ + dbus_bindings/dbus-service-config.json \ + dbus_bindings/org.chromium.Firewalld.dbus-xml \ + binder_bindings/android/firewalld/IFirewall.aidl \ + binder_interface.cc \ + firewall_daemon.cc \ + firewall_service.cc \ + iptables.cc +$(eval $(firewalld_common)) +include $(BUILD_STATIC_LIBRARY) + +# === firewalld === +include $(CLEAR_VARS) +LOCAL_MODULE := firewalld +LOCAL_INIT_RC := firewalld.rc +LOCAL_SRC_FILES := \ + main.cc +LOCAL_STATIC_LIBRARIES := libfirewalld +$(eval $(firewalld_common)) +include $(BUILD_EXECUTABLE) + +# === unittest === +include $(CLEAR_VARS) +LOCAL_MODULE := firewalld_unittest +LOCAL_SRC_FILES := \ + iptables_unittest.cc \ + mock_iptables.cc \ + run_all_tests.cc +LOCAL_STATIC_LIBRARIES := libfirewalld libgmock +$(eval $(firewalld_common)) +include $(BUILD_NATIVE_TEST)
diff --git a/firewalld/MODULE_LICENSE_APACHE2 b/firewalld/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/firewalld/MODULE_LICENSE_APACHE2
diff --git a/firewalld/NOTICE b/firewalld/NOTICE new file mode 100644 index 0000000..a849a94 --- /dev/null +++ b/firewalld/NOTICE
@@ -0,0 +1,190 @@ + + Copyright (c) 2014-2015, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +
diff --git a/firewalld/OWNERS b/firewalld/OWNERS new file mode 100644 index 0000000..14589ff --- /dev/null +++ b/firewalld/OWNERS
@@ -0,0 +1,2 @@ +jorgelo@chromium.org +keescook@chromium.org
diff --git a/firewalld/binder_bindings/android/firewalld/IFirewall.aidl b/firewalld/binder_bindings/android/firewalld/IFirewall.aidl new file mode 100644 index 0000000..45c58c4 --- /dev/null +++ b/firewalld/binder_bindings/android/firewalld/IFirewall.aidl
@@ -0,0 +1,25 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android.firewalld; + +@utf8InCpp +interface IFirewall { + void PunchTcpHole(int port, String iface); + void PunchUdpHole(int port, String iface); + void PlugTcpHole(int port, String iface); + void PlugUdpHole(int port, String iface); + void RequestVpnSetup(in String[] usernames, String iface); + void RemoveVpnSetup(in String[] usernames, String iface); +}
diff --git a/firewalld/binder_interface.cc b/firewalld/binder_interface.cc new file mode 100644 index 0000000..6205770 --- /dev/null +++ b/firewalld/binder_interface.cc
@@ -0,0 +1,78 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "binder_interface.h" +#include "iptables.h" + +using android::binder::Status; + +using std::string; +using std::vector; + +namespace firewalld { + +BinderInterface::BinderInterface(IpTables* service) + : service_(service) {} + +Status BinderInterface::PunchTcpHole(int32_t port, const string& iface) { + if (service_->PunchTcpHole(port, iface)) { + return Status::ok(); + } + + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); +} + +Status BinderInterface::PunchUdpHole(int32_t port, const string& iface) { + if (service_->PunchUdpHole(port, iface)) { + return Status::ok(); + } + + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); +} + +Status BinderInterface::PlugTcpHole(int32_t port, const string& iface) { + if (service_->PlugTcpHole(port, iface)) { + return Status::ok(); + } + + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); +} + +Status BinderInterface::PlugUdpHole(int32_t port, const string& iface) { + if (service_->PlugUdpHole(port, iface)) { + return Status::ok(); + } + + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); +} + +Status BinderInterface::RequestVpnSetup(const vector<string>& usernames, + const string& iface) { + if (service_->RequestVpnSetup(usernames, iface)) { + return Status::ok(); + } + + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); +} + +Status BinderInterface::RemoveVpnSetup(const vector<string>& usernames, + const string& iface) { + if (service_->RemoveVpnSetup(usernames, iface)) { + return Status::ok(); + } + + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); +} + +} // namespace firewalld
diff --git a/firewalld/binder_interface.h b/firewalld/binder_interface.h new file mode 100644 index 0000000..01600b9 --- /dev/null +++ b/firewalld/binder_interface.h
@@ -0,0 +1,53 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREWALLD_BINDER_INTERFACE_H_ +#define FIREWALLD_BINDER_INTERFACE_H_ + +#include <base/macros.h> + +#include "android/firewalld/BnFirewall.h" + +namespace firewalld { + +class IpTables; +class BinderInterface : public android::firewalld::BnFirewall { + public: + explicit BinderInterface(IpTables* service); + virtual ~BinderInterface() = default; + + android::binder::Status PunchTcpHole(int32_t port, const std::string& iface) + override; + android::binder::Status PunchUdpHole(int32_t port, const std::string& iface) + override; + android::binder::Status PlugTcpHole(int32_t port, const std::string& iface) + override; + android::binder::Status PlugUdpHole(int32_t port, const std::string& iface) + override; + android::binder::Status RequestVpnSetup( + const std::vector<std::string>& usernames, + const std::string& iface) override; + android::binder::Status RemoveVpnSetup( + const std::vector<std::string>& usernames, + const std::string& iface) override; + + private: + IpTables* service_; + + DISALLOW_COPY_AND_ASSIGN(BinderInterface); +}; + +} // namespace firewalld + +#endif /* FIREWALLD_BINDER_INTERFACE_H_ */
diff --git a/firewalld/dbus/org.chromium.Firewalld.conf b/firewalld/dbus/org.chromium.Firewalld.conf new file mode 100644 index 0000000..000b74e --- /dev/null +++ b/firewalld/dbus/org.chromium.Firewalld.conf
@@ -0,0 +1,18 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="root"> + <allow own="org.chromium.Firewalld"/> + </policy> + + <policy user="devbroker"> + <allow send_destination="org.chromium.Firewalld"/> + </policy> + + <policy context="default"> + <deny send_destination="org.chromium.Firewalld"/> + </policy> + + <limit name="max_replies_per_connection">512</limit> +</busconfig>
diff --git a/firewalld/dbus_bindings/dbus-service-config.json b/firewalld/dbus_bindings/dbus-service-config.json new file mode 100644 index 0000000..f62c2d2 --- /dev/null +++ b/firewalld/dbus_bindings/dbus-service-config.json
@@ -0,0 +1,6 @@ +{ + "service_name": "org.chromium.Firewalld", + "object_manager": { + "object_path": "/org/chromium/Firewalld" + } +}
diff --git a/firewalld/dbus_bindings/org.chromium.Firewalld.dbus-xml b/firewalld/dbus_bindings/org.chromium.Firewalld.dbus-xml new file mode 100644 index 0000000..97ea215 --- /dev/null +++ b/firewalld/dbus_bindings/org.chromium.Firewalld.dbus-xml
@@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8" ?> +<node name="/org/chromium/Firewalld/Firewall"> + <interface name="org.chromium.Firewalld"> + <method name="PunchTcpHole"> + <arg type="q" name="port" direction="in" /> + <arg type="s" name="interface" direction="in"/> + <arg type="b" name="success" direction="out" /> + <annotation name="org.chromium.DBus.Method.Kind" value="simple"/> + </method> + <method name="PunchUdpHole"> + <arg type="q" name="port" direction="in" /> + <arg type="s" name="interface" direction="in"/> + <arg type="b" name="success" direction="out" /> + <annotation name="org.chromium.DBus.Method.Kind" value="simple"/> + </method> + <method name="PlugTcpHole"> + <arg type="q" name="port" direction="in" /> + <arg type="s" name="interface" direction="in"/> + <arg type="b" name="success" direction="out" /> + <annotation name="org.chromium.DBus.Method.Kind" value="simple"/> + </method> + <method name="PlugUdpHole"> + <arg type="q" name="port" direction="in" /> + <arg type="s" name="interface" direction="in"/> + <arg type="b" name="success" direction="out" /> + <annotation name="org.chromium.DBus.Method.Kind" value="simple"/> + </method> + <method name="RequestVpnSetup"> + <arg type="as" name="usernames" direction="in" /> + <arg type="s" name="interface" direction="in" /> + <arg type="b" name="success" direction="out" /> + <annotation name="org.chromium.DBus.Method.Kind" value="simple"/> + </method> + <method name="RemoveVpnSetup"> + <arg type="as" name="usernames" direction="in" /> + <arg type="s" name="interface" direction="in" /> + <arg type="b" name="success" direction="out" /> + <annotation name="org.chromium.DBus.Method.Kind" value="simple"/> + </method> + </interface> +</node>
diff --git a/firewalld/dbus_interface.h b/firewalld/dbus_interface.h new file mode 100644 index 0000000..829d666 --- /dev/null +++ b/firewalld/dbus_interface.h
@@ -0,0 +1,25 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREWALLD_DBUS_INTERFACE_H_ +#define FIREWALLD_DBUS_INTERFACE_H_ + +namespace firewalld { + +const char kFirewallServicePath[] = "/org/chromium/Firewalld"; +const char kFirewallServiceName[] = "org.chromium.Firewalld"; + +} // namespace firewalld + +#endif // FIREWALLD_DBUS_INTERFACE_H_
diff --git a/firewalld/firewall_binder_client.cc b/firewalld/firewall_binder_client.cc new file mode 100644 index 0000000..b29f49c --- /dev/null +++ b/firewalld/firewall_binder_client.cc
@@ -0,0 +1,98 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <base/logging.h> +#include <firewalld/firewall.h> + +#include <unistd.h> + +#include "android/firewalld/IFirewall.h" + +#define FIREWALL_BINDER_CLIENT_EXPORT __attribute__((__visibility__("default"))) + +using std::unique_ptr; +using std::vector; +using std::string; + +using android::firewalld::IFirewall; +using android::IBinder; +using android::sp; + +namespace firewalld { + +const char kServiceName[] = "android.firewalld.Firewall"; + +class FirewallImpl : public Firewall { + public: + explicit FirewallImpl(sp<IBinder> server_firewall) + : server_firewall_(android::interface_cast<IFirewall>(server_firewall)) {} + virtual ~FirewallImpl() = default; + + bool PunchTcpHole(int16_t port, const string& iface) override { + return server_firewall_->PunchTcpHole(port, iface).isOk(); + } + + bool PunchUdpHole(int16_t port, const string& iface) override { + return server_firewall_->PunchUdpHole(port, iface).isOk(); + } + + bool PlugTcpHole(int16_t port, const string& iface) override { + return server_firewall_->PlugTcpHole(port, iface).isOk(); + } + + bool PlugUdpHole(int16_t port, const string& iface) override { + return server_firewall_->PlugUdpHole(port, iface).isOk(); + } + + bool RequestVpnSetup(const vector<string>& usernames, + const string& iface) override { + return server_firewall_->RequestVpnSetup(usernames, iface).isOk(); + } + + bool RemoveVpnSetup(const vector<string>& usernames, + const string& iface) override { + return server_firewall_->RemoveVpnSetup(usernames, iface).isOk(); + } + + private: + sp<IFirewall> server_firewall_; + + DISALLOW_COPY_AND_ASSIGN(FirewallImpl); +}; + +FIREWALL_BINDER_CLIENT_EXPORT +unique_ptr<Firewall> Firewall::Connect( + android::BinderWrapper* binder_wrapper) { + sp<IBinder> binder(nullptr); + int retries = 15; + + while (retries--) { + binder = binder_wrapper->GetService(kServiceName); + + if (binder.get()) { + break; + } + + sleep(1); + } + + if (!binder.get()) { + LOG(WARNING) << "Could not connect to " << kServiceName << " service."; + return unique_ptr<Firewall>(); + } + + return unique_ptr<Firewall>(new FirewallImpl(binder)); +} + +} // namespace firewalld
diff --git a/firewalld/firewall_daemon.cc b/firewalld/firewall_daemon.cc new file mode 100644 index 0000000..ae12e5a --- /dev/null +++ b/firewalld/firewall_daemon.cc
@@ -0,0 +1,59 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firewall_daemon.h" + +#include <string> + +#include <base/logging.h> + +#ifdef __ANDROID__ +#include <sysexits.h> +#include <binderwrapper/binder_wrapper.h> +#endif // __ANDROID__ + +namespace firewalld { + +#ifdef __ANDROID__ +const char kServiceName[] = "android.firewalld.Firewall"; +#endif // __ANDROID__ + +FirewallDaemon::FirewallDaemon() + : brillo::DBusServiceDaemon{kFirewallServiceName, + dbus::ObjectPath{kFirewallServicePath}} { +} + +void FirewallDaemon::RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) { + firewall_service_.reset( + new firewalld::FirewallService{object_manager_.get()}); + firewall_service_->RegisterAsync( + sequencer->GetHandler("Service.RegisterAsync() failed.", true)); + +#ifdef __ANDROID__ + android::BinderWrapper::Create(); + if (!binder_watcher_.Init()) { + LOG(ERROR) << "Could not initialize binder watcher."; + return; + } + + if (!android::BinderWrapper::Get()->RegisterService( + kServiceName, + firewall_service_->GetBinderInterface())) { + LOG(ERROR) << "Could not register service " << kServiceName << "."; + } +#endif // __ANDROID__ + +} + +} // namespace firewalld
diff --git a/firewalld/firewall_daemon.h b/firewalld/firewall_daemon.h new file mode 100644 index 0000000..89ed4ad --- /dev/null +++ b/firewalld/firewall_daemon.h
@@ -0,0 +1,51 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREWALLD_FIREWALL_DAEMON_H_ +#define FIREWALLD_FIREWALL_DAEMON_H_ + +#include <base/macros.h> +#include <brillo/daemons/dbus_daemon.h> +#include <brillo/dbus/async_event_sequencer.h> + +#ifdef __ANDROID__ +#include <brillo/binder_watcher.h> +#endif // __ANDROID__ + +#include "dbus_interface.h" +#include "firewall_service.h" + +using brillo::dbus_utils::AsyncEventSequencer; + +namespace firewalld { + +class FirewallDaemon : public brillo::DBusServiceDaemon { + public: + FirewallDaemon(); + + protected: + void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override; + + private: +#ifdef __ANDROID__ + brillo::BinderWatcher binder_watcher_; +#endif // __ANDROID__ + std::unique_ptr<FirewallService> firewall_service_; + + DISALLOW_COPY_AND_ASSIGN(FirewallDaemon); +}; + +} // namespace firewalld + +#endif // FIREWALLD_FIREWALL_DAEMON_H_
diff --git a/firewalld/firewall_service.cc b/firewalld/firewall_service.cc new file mode 100644 index 0000000..b339e96 --- /dev/null +++ b/firewalld/firewall_service.cc
@@ -0,0 +1,64 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firewall_service.h" + +#include "dbus_interface.h" +#include "iptables.h" + +namespace firewalld { + +FirewallService::FirewallService( + brillo::dbus_utils::ExportedObjectManager* object_manager) + : org::chromium::FirewalldAdaptor(&iptables_), + dbus_object_{object_manager, object_manager->GetBus(), + org::chromium::FirewalldAdaptor::GetObjectPath()} {} + +void FirewallService::RegisterAsync(const CompletionAction& callback) { + RegisterWithDBusObject(&dbus_object_); + +#if !defined(__ANDROID__) + // Track permission_broker's lifetime so that we can close firewall holes + // if/when permission_broker exits. + permission_broker_.reset( + new org::chromium::PermissionBroker::ObjectManagerProxy( + dbus_object_.GetBus())); + permission_broker_->SetPermissionBrokerRemovedCallback( + base::Bind(&FirewallService::OnPermissionBrokerRemoved, + weak_ptr_factory_.GetWeakPtr())); +#endif // __ANDROID__ + + dbus_object_.RegisterAsync(callback); +} + +#ifdef __ANDROID__ + // Get the binder interface for this service +android::sp<BinderInterface> FirewallService::GetBinderInterface() { + if (!binder_interface_.get()) { + binder_interface_ = + android::sp<BinderInterface>(new BinderInterface(&iptables_)); + } + + return binder_interface_; +} +#endif // __ANDROID__ + +#if !defined(__ANDROID__) +void FirewallService::OnPermissionBrokerRemoved(const dbus::ObjectPath& path) { + LOG(INFO) << "permission_broker died, plugging all firewall holes"; + iptables_.PlugAllHoles(); +} +#endif // __ANDROID__ + +} // namespace firewalld
diff --git a/firewalld/firewall_service.h b/firewalld/firewall_service.h new file mode 100644 index 0000000..bcfb546 --- /dev/null +++ b/firewalld/firewall_service.h
@@ -0,0 +1,80 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREWALLD_FIREWALL_SERVICE_H_ +#define FIREWALLD_FIREWALL_SERVICE_H_ + +#if !defined(__ANDROID__) +# include <memory> +#endif // !__ANDROID__ + +#include <base/callback.h> +#include <base/macros.h> +#include <base/memory/weak_ptr.h> +#include <brillo/dbus/dbus_object.h> + +#include "dbus_bindings/org.chromium.Firewalld.h" +#if !defined(__ANDROID__) +# include "permission_broker/dbus-proxies.h" +#endif // __ANDROID__ + +#ifdef __ANDROID__ +#include "binder_interface.h" +#endif // __ANDROID__ + +#include "iptables.h" + +using CompletionAction = + brillo::dbus_utils::AsyncEventSequencer::CompletionAction; + +namespace firewalld { + +class FirewallService : public org::chromium::FirewalldAdaptor { + public: + explicit FirewallService( + brillo::dbus_utils::ExportedObjectManager* object_manager); + virtual ~FirewallService() = default; + + // Connects to D-Bus system bus and exports methods. + void RegisterAsync(const CompletionAction& callback); + +#ifdef __ANDROID__ + // Get the binder interface for this firewall. + android::sp<BinderInterface> GetBinderInterface(); +#endif // __ANDROID__ + + private: +#if !defined(__ANDROID__) + void OnPermissionBrokerRemoved(const dbus::ObjectPath& path); +#endif // __ANDROID__ + + brillo::dbus_utils::DBusObject dbus_object_; +#if !defined(__ANDROID__) + std::unique_ptr<org::chromium::PermissionBroker::ObjectManagerProxy> + permission_broker_; +#endif // __ANDROID__ + IpTables iptables_; + +#ifdef __ANDROID__ + // A binder interface for this firewall. + android::sp<BinderInterface> binder_interface_; +#endif // __ANDROID__ + + base::WeakPtrFactory<FirewallService> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(FirewallService); +}; + +} // namespace firewalld + +#endif // FIREWALLD_FIREWALL_SERVICE_H_
diff --git a/firewalld/firewalld.conf b/firewalld/firewalld.conf new file mode 100644 index 0000000..bfa521a --- /dev/null +++ b/firewalld/firewalld.conf
@@ -0,0 +1,24 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +description "Firewall daemon" +author "chromium-os-dev@chromium.org" + +start on stopped iptables and stopped ip6tables +stop on stopping system-services +respawn + +exec firewalld
diff --git a/firewalld/firewalld.gyp b/firewalld/firewalld.gyp new file mode 100644 index 0000000..4b110e8 --- /dev/null +++ b/firewalld/firewalld.gyp
@@ -0,0 +1,85 @@ +{ + 'target_defaults': { + 'variables': { + 'deps': [ + 'libbrillo-<(libbase_ver)', + 'libchrome-<(libbase_ver)', + 'libpermission_broker-client', + ], + }, + }, + 'targets': [ + { + 'target_name': 'libfirewalld', + 'type': 'static_library', + 'sources': [ + 'firewall_daemon.cc', + 'firewall_service.cc', + 'iptables.cc', + ], + }, + { + 'target_name': 'firewalld-dbus-adaptor', + 'type': 'none', + 'variables': { + 'dbus_service_config': 'dbus_bindings/dbus-service-config.json', + 'dbus_adaptors_out_dir': 'include/dbus_bindings', + 'dbus_xml_extension': 'dbus-xml', + }, + 'sources': [ + 'dbus_bindings/org.chromium.Firewalld.dbus-xml', + ], + 'includes': ['../common-mk/generate-dbus-adaptors.gypi'], + }, + { + 'target_name': 'firewalld-dbus-proxies', + 'type': 'none', + 'actions': [ + { + 'action_name': 'generate-firewalld-dbus-proxies', + 'variables': { + 'dbus_service_config': 'dbus_bindings/dbus-service-config.json', + 'proxy_output_file': 'include/firewalld/dbus-proxies.h', + 'mock_output_file': 'include/firewalld/dbus-mocks.h', + 'proxy_path_in_mocks': 'firewalld/dbus-proxies.h', + }, + 'sources': [ + 'dbus_bindings/org.chromium.Firewalld.dbus-xml', + ], + 'includes': ['../common-mk/generate-dbus-proxies.gypi'], + }, + ], + }, + { + 'target_name': 'firewalld', + 'type': 'executable', + 'dependencies': [ + 'libfirewalld', + 'firewalld-dbus-adaptor', + ], + 'sources': ['main.cc'], + }, + ], + 'conditions': [ + ['USE_test == 1', { + 'targets': [ + { + 'target_name': 'firewalld_unittest', + 'type': 'executable', + 'variables': { + 'deps': [ + 'libpermission_broker-client-test', + ], + }, + 'includes': ['../common-mk/common_test.gypi'], + 'dependencies': ['libfirewalld'], + 'sources': [ + 'iptables_unittest.cc', + 'mock_iptables.cc', + 'run_all_tests.cc', + ], + }, + ], + }], + ], +}
diff --git a/firewalld/firewalld.rc b/firewalld/firewalld.rc new file mode 100644 index 0000000..b3da589 --- /dev/null +++ b/firewalld/firewalld.rc
@@ -0,0 +1,5 @@ +service firewalld /system/bin/firewalld + class main + user firewall + group firewall dbus + disabled
diff --git a/firewalld/generate_pc_file.sh b/firewalld/generate_pc_file.sh new file mode 100755 index 0000000..82f8740 --- /dev/null +++ b/firewalld/generate_pc_file.sh
@@ -0,0 +1,18 @@ +#!/bin/bash + +# Copyright 2015 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +OUT=$1 +shift +PC_IN=$1 +shift +INCLUDE_DIR=$1 +shift + +sed \ + -e "s|@INCLUDE_DIR@|${INCLUDE_DIR}|g" \ + "${PC_IN}.pc.in" > "${OUT}/${PC_IN}.pc"
diff --git a/firewalld/include/firewalld/firewall.h b/firewalld/include/firewalld/firewall.h new file mode 100644 index 0000000..04718fa --- /dev/null +++ b/firewalld/include/firewalld/firewall.h
@@ -0,0 +1,49 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREWALLD_BINDER_CLIENT_FIREWALL_H_ +#define FIREWALLD_BINDER_CLIENT_FIREWALL_H_ + +#include <memory> +#include <string> +#include <vector> + +#include <binderwrapper/binder_wrapper.h> + +namespace firewalld { + +class Firewall { + public: + Firewall() = default; + virtual ~Firewall() = default; + + static std::unique_ptr<Firewall> Connect( + android::BinderWrapper* binder_wrapper); + + virtual bool PunchTcpHole(int16_t port, const std::string& iface) = 0; + virtual bool PunchUdpHole(int16_t port, const std::string& iface) = 0; + virtual bool PlugTcpHole(int16_t port, const std::string& iface) = 0; + virtual bool PlugUdpHole(int16_t port, const std::string& iface) = 0; + virtual bool RequestVpnSetup(const std::vector<std::string>& usernames, + const std::string& iface) = 0; + virtual bool RemoveVpnSetup(const std::vector<std::string>& usernames, + const std::string& iface) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(Firewall); +}; + +} // namespace firewalld + +#endif /* FIREWALLD_BINDER_CLIENT_FIREWALL_H_ */
diff --git a/firewalld/iptables.cc b/firewalld/iptables.cc new file mode 100644 index 0000000..62201dd --- /dev/null +++ b/firewalld/iptables.cc
@@ -0,0 +1,472 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "iptables.h" + +#include <linux/capability.h> + +#include <string> +#include <vector> + +#include <base/bind.h> +#include <base/bind_helpers.h> +#include <base/callback.h> +#include <base/logging.h> +#include <base/strings/string_number_conversions.h> +#include <base/strings/string_util.h> +#include <base/strings/stringprintf.h> +#include <brillo/minijail/minijail.h> +#include <brillo/process.h> + +namespace { + +using IpTablesCallback = base::Callback<bool(const std::string&, bool)>; + +#if defined(__ANDROID__) +const char kIpTablesPath[] = "/system/bin/iptables"; +const char kIp6TablesPath[] = "/system/bin/ip6tables"; +const char kIpPath[] = "/system/bin/ip"; +#else +const char kIpTablesPath[] = "/sbin/iptables"; +const char kIp6TablesPath[] = "/sbin/ip6tables"; +const char kIpPath[] = "/bin/ip"; +const char kUnprivilegedUser[] = "nobody"; +#endif // __ANDROID__ + +const char kIPv4[] = "IPv4"; +const char kIPv6[] = "IPv6"; + +const uint64_t kIpTablesCapMask = + CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_RAW); + +// Interface names must be shorter than 'IFNAMSIZ' chars. +// See http://man7.org/linux/man-pages/man7/netdevice.7.html +// 'IFNAMSIZ' is 16 in recent kernels. +// See http://lxr.free-electrons.com/source/include/uapi/linux/if.h#L26 +const size_t kInterfaceNameSize = 16; + +const char kMarkForUserTraffic[] = "1"; + +const char kTableIdForUserTraffic[] = "1"; + +bool IsValidInterfaceName(const std::string& iface) { + // |iface| should be shorter than |kInterfaceNameSize| chars and have only + // alphanumeric characters (embedded hypens and periods are also permitted). + if (iface.length() >= kInterfaceNameSize) { + return false; + } + if (base::StartsWith(iface, "-", base::CompareCase::SENSITIVE) || + base::EndsWith(iface, "-", base::CompareCase::SENSITIVE) || + base::StartsWith(iface, ".", base::CompareCase::SENSITIVE) || + base::EndsWith(iface, ".", base::CompareCase::SENSITIVE)) { + return false; + } + for (auto c : iface) { + if (!std::isalnum(c) && (c != '-') && (c != '.')) { + return false; + } + } + return true; +} + +bool RunForAllArguments(const IpTablesCallback& iptables_cmd, + const std::vector<std::string>& arguments, + bool add) { + bool success = true; + for (const auto& argument : arguments) { + if (!iptables_cmd.Run(argument, add)) { + // On failure, only abort if rules are being added. + // If removing a rule fails, attempt the remaining removals but still + // return 'false'. + success = false; + if (add) + break; + } + } + return success; +} + +} // namespace + +namespace firewalld { + +IpTables::IpTables() { +} + +IpTables::~IpTables() { + // Plug all holes when destructed. + PlugAllHoles(); +} + +bool IpTables::PunchTcpHole(uint16_t in_port, const std::string& in_interface) { + return PunchHole(in_port, in_interface, &tcp_holes_, kProtocolTcp); +} + +bool IpTables::PunchUdpHole(uint16_t in_port, const std::string& in_interface) { + return PunchHole(in_port, in_interface, &udp_holes_, kProtocolUdp); +} + +bool IpTables::PlugTcpHole(uint16_t in_port, const std::string& in_interface) { + return PlugHole(in_port, in_interface, &tcp_holes_, kProtocolTcp); +} + +bool IpTables::PlugUdpHole(uint16_t in_port, const std::string& in_interface) { + return PlugHole(in_port, in_interface, &udp_holes_, kProtocolUdp); +} + +bool IpTables::RequestVpnSetup(const std::vector<std::string>& usernames, + const std::string& interface) { + return ApplyVpnSetup(usernames, interface, true /* add */); +} + +bool IpTables::RemoveVpnSetup(const std::vector<std::string>& usernames, + const std::string& interface) { + return ApplyVpnSetup(usernames, interface, false /* delete */); +} + +bool IpTables::PunchHole(uint16_t port, + const std::string& interface, + std::set<Hole>* holes, + ProtocolEnum protocol) { + if (port == 0) { + // Port 0 is not a valid TCP/UDP port. + return false; + } + + if (!IsValidInterfaceName(interface)) { + LOG(ERROR) << "Invalid interface name '" << interface << "'"; + return false; + } + + Hole hole = std::make_pair(port, interface); + if (holes->find(hole) != holes->end()) { + // We have already punched a hole for |port| on |interface|. + // Be idempotent: do nothing and succeed. + return true; + } + + std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP"; + LOG(INFO) << "Punching hole for " << sprotocol << " port " << port + << " on interface '" << interface << "'"; + if (!AddAcceptRules(protocol, port, interface)) { + // If the 'iptables' command fails, this method fails. + LOG(ERROR) << "Adding ACCEPT rules failed."; + return false; + } + + // Track the hole we just punched. + holes->insert(hole); + + return true; +} + +bool IpTables::PlugHole(uint16_t port, + const std::string& interface, + std::set<Hole>* holes, + ProtocolEnum protocol) { + if (port == 0) { + // Port 0 is not a valid TCP/UDP port. + return false; + } + + Hole hole = std::make_pair(port, interface); + + if (holes->find(hole) == holes->end()) { + // There is no firewall hole for |port| on |interface|. + // Even though this makes |PlugHole| not idempotent, + // and Punch/Plug not entirely symmetrical, fail. It might help catch bugs. + return false; + } + + std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP"; + LOG(INFO) << "Plugging hole for " << sprotocol << " port " << port + << " on interface '" << interface << "'"; + if (!DeleteAcceptRules(protocol, port, interface)) { + // If the 'iptables' command fails, this method fails. + LOG(ERROR) << "Deleting ACCEPT rules failed."; + return false; + } + + // Stop tracking the hole we just plugged. + holes->erase(hole); + + return true; +} + +void IpTables::PlugAllHoles() { + // Copy the container so that we can remove elements from the original. + // TCP + std::set<Hole> holes = tcp_holes_; + for (auto hole : holes) { + PlugHole(hole.first /* port */, hole.second /* interface */, &tcp_holes_, + kProtocolTcp); + } + + // UDP + holes = udp_holes_; + for (auto hole : holes) { + PlugHole(hole.first /* port */, hole.second /* interface */, &udp_holes_, + kProtocolUdp); + } + + CHECK(tcp_holes_.size() == 0) << "Failed to plug all TCP holes."; + CHECK(udp_holes_.size() == 0) << "Failed to plug all UDP holes."; +} + +bool IpTables::AddAcceptRules(ProtocolEnum protocol, + uint16_t port, + const std::string& interface) { + if (!AddAcceptRule(kIpTablesPath, protocol, port, interface)) { + LOG(ERROR) << "Could not add ACCEPT rule using '" << kIpTablesPath << "'"; + return false; + } + + if (AddAcceptRule(kIp6TablesPath, protocol, port, interface)) { + // This worked, record this fact and insist that it works thereafter. + ip6_enabled_ = true; + } else if (ip6_enabled_) { + // It's supposed to work, fail. + LOG(ERROR) << "Could not add ACCEPT rule using '" << kIp6TablesPath + << "', aborting operation."; + DeleteAcceptRule(kIpTablesPath, protocol, port, interface); + return false; + } else { + // It never worked, just ignore it. + LOG(WARNING) << "Could not add ACCEPT rule using '" << kIp6TablesPath + << "', ignoring."; + } + + return true; +} + +bool IpTables::DeleteAcceptRules(ProtocolEnum protocol, + uint16_t port, + const std::string& interface) { + bool ip4_success = DeleteAcceptRule(kIpTablesPath, protocol, port, + interface); + bool ip6_success = !ip6_enabled_ || DeleteAcceptRule(kIp6TablesPath, protocol, + port, interface); + return ip4_success && ip6_success; +} + +bool IpTables::ApplyVpnSetup(const std::vector<std::string>& usernames, + const std::string& interface, + bool add) { + bool success = true; + std::vector<std::string> added_usernames; + + if (!ApplyRuleForUserTraffic(add)) { + if (add) { + ApplyRuleForUserTraffic(false /* remove */); + return false; + } + success = false; + } + + if (!ApplyMasquerade(interface, add)) { + if (add) { + ApplyVpnSetup(added_usernames, interface, false /* remove */); + return false; + } + success = false; + } + + for (const auto& username : usernames) { + if (!ApplyMarkForUserTraffic(username, add)) { + if (add) { + ApplyVpnSetup(added_usernames, interface, false /* remove */); + return false; + } + success = false; + } + if (add) { + added_usernames.push_back(username); + } + } + + return success; +} + +bool IpTables::ApplyMasquerade(const std::string& interface, bool add) { + const IpTablesCallback apply_masquerade = + base::Bind(&IpTables::ApplyMasqueradeWithExecutable, + base::Unretained(this), + interface); + + return RunForAllArguments( + apply_masquerade, {kIpTablesPath, kIp6TablesPath}, add); +} + +bool IpTables::ApplyMarkForUserTraffic(const std::string& username, bool add) { + const IpTablesCallback apply_mark = + base::Bind(&IpTables::ApplyMarkForUserTrafficWithExecutable, + base::Unretained(this), + username); + + return RunForAllArguments(apply_mark, {kIpTablesPath, kIp6TablesPath}, add); +} + +bool IpTables::ApplyRuleForUserTraffic(bool add) { + const IpTablesCallback apply_rule = base::Bind( + &IpTables::ApplyRuleForUserTrafficWithVersion, base::Unretained(this)); + + return RunForAllArguments(apply_rule, {kIPv4, kIPv6}, add); +} + +bool IpTables::AddAcceptRule(const std::string& executable_path, + ProtocolEnum protocol, + uint16_t port, + const std::string& interface) { + std::vector<std::string> argv; + argv.push_back(executable_path); + argv.push_back("-I"); // insert + argv.push_back("INPUT"); + argv.push_back("-p"); // protocol + argv.push_back(protocol == kProtocolTcp ? "tcp" : "udp"); + argv.push_back("--dport"); // destination port + argv.push_back(std::to_string(port)); + if (!interface.empty()) { + argv.push_back("-i"); // interface + argv.push_back(interface); + } + argv.push_back("-j"); + argv.push_back("ACCEPT"); + argv.push_back("-w"); // Wait for xtables lock. + + // Use CAP_NET_ADMIN|CAP_NET_RAW. + return ExecvNonRoot(argv, kIpTablesCapMask) == 0; +} + +bool IpTables::DeleteAcceptRule(const std::string& executable_path, + ProtocolEnum protocol, + uint16_t port, + const std::string& interface) { + std::vector<std::string> argv; + argv.push_back(executable_path); + argv.push_back("-D"); // delete + argv.push_back("INPUT"); + argv.push_back("-p"); // protocol + argv.push_back(protocol == kProtocolTcp ? "tcp" : "udp"); + argv.push_back("--dport"); // destination port + argv.push_back(std::to_string(port)); + if (interface != "") { + argv.push_back("-i"); // interface + argv.push_back(interface); + } + argv.push_back("-j"); + argv.push_back("ACCEPT"); + argv.push_back("-w"); // Wait for xtables lock. + + // Use CAP_NET_ADMIN|CAP_NET_RAW. + return ExecvNonRoot(argv, kIpTablesCapMask) == 0; +} + +bool IpTables::ApplyMasqueradeWithExecutable(const std::string& interface, + const std::string& executable_path, + bool add) { + std::vector<std::string> argv; + argv.push_back(executable_path); + argv.push_back("-t"); // table + argv.push_back("nat"); + argv.push_back(add ? "-A" : "-D"); // rule + argv.push_back("POSTROUTING"); + argv.push_back("-o"); // output interface + argv.push_back(interface); + argv.push_back("-j"); + argv.push_back("MASQUERADE"); + + // Use CAP_NET_ADMIN|CAP_NET_RAW. + bool success = ExecvNonRoot(argv, kIpTablesCapMask) == 0; + + if (!success) { + LOG(ERROR) << (add ? "Adding" : "Removing") + << " masquerade failed for interface " << interface + << " using '" << executable_path << "'"; + } + return success; +} + +bool IpTables::ApplyMarkForUserTrafficWithExecutable( + const std::string& username, const std::string& executable_path, bool add) { + std::vector<std::string> argv; + argv.push_back(executable_path); + argv.push_back("-t"); // table + argv.push_back("mangle"); + argv.push_back(add ? "-A" : "-D"); // rule + argv.push_back("OUTPUT"); + argv.push_back("-m"); + argv.push_back("owner"); + argv.push_back("--uid-owner"); + argv.push_back(username); + argv.push_back("-j"); + argv.push_back("MARK"); + argv.push_back("--set-mark"); + argv.push_back(kMarkForUserTraffic); + + // Use CAP_NET_ADMIN|CAP_NET_RAW. + bool success = ExecvNonRoot(argv, kIpTablesCapMask) == 0; + + if (!success) { + LOG(ERROR) << (add ? "Adding" : "Removing") + << " mark failed for user " << username + << " using '" << kIpTablesPath << "'"; + } + return success; +} + +bool IpTables::ApplyRuleForUserTrafficWithVersion(const std::string& ip_version, + bool add) { + brillo::ProcessImpl ip; + ip.AddArg(kIpPath); + if (ip_version == kIPv6) + ip.AddArg("-6"); + ip.AddArg("rule"); + ip.AddArg(add ? "add" : "delete"); + ip.AddArg("fwmark"); + ip.AddArg(kMarkForUserTraffic); + ip.AddArg("table"); + ip.AddArg(kTableIdForUserTraffic); + + bool success = ip.Run() == 0; + + if (!success) { + LOG(ERROR) << (add ? "Adding" : "Removing") << " rule for " << ip_version + << " user traffic failed"; + } + return success; +} + +int IpTables::ExecvNonRoot(const std::vector<std::string>& argv, + uint64_t capmask) { + brillo::Minijail* m = brillo::Minijail::GetInstance(); + minijail* jail = m->New(); +#if !defined(__ANDROID__) + // TODO(garnold) This needs to be re-enabled once we figure out which + // unprivileged user we want to use. + m->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser); +#endif // __ANDROID__ + m->UseCapabilities(jail, capmask); + + std::vector<char*> args; + for (const auto& arg : argv) { + args.push_back(const_cast<char*>(arg.c_str())); + } + args.push_back(nullptr); + + int status; + bool ran = m->RunSyncAndDestroy(jail, args, &status); + return ran ? status : -1; +} + +} // namespace firewalld
diff --git a/firewalld/iptables.h b/firewalld/iptables.h new file mode 100644 index 0000000..74b9acb --- /dev/null +++ b/firewalld/iptables.h
@@ -0,0 +1,124 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREWALLD_IPTABLES_H_ +#define FIREWALLD_IPTABLES_H_ + +#include <stdint.h> + +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include <base/macros.h> +#include <brillo/errors/error.h> + +#include "dbus_bindings/org.chromium.Firewalld.h" + +namespace firewalld { + +enum ProtocolEnum { kProtocolTcp, kProtocolUdp }; + +class IpTables : public org::chromium::FirewalldInterface { + public: + typedef std::pair<uint16_t, std::string> Hole; + + IpTables(); + ~IpTables(); + + // D-Bus methods. + bool PunchTcpHole(uint16_t in_port, const std::string& in_interface) override; + bool PunchUdpHole(uint16_t in_port, const std::string& in_interface) override; + bool PlugTcpHole(uint16_t in_port, const std::string& in_interface) override; + bool PlugUdpHole(uint16_t in_port, const std::string& in_interface) override; + + bool RequestVpnSetup(const std::vector<std::string>& usernames, + const std::string& interface) override; + bool RemoveVpnSetup(const std::vector<std::string>& usernames, + const std::string& interface) override; + + // Close all outstanding firewall holes. + void PlugAllHoles(); + + private: + friend class IpTablesTest; + FRIEND_TEST(IpTablesTest, ApplyVpnSetupAdd_Success); + FRIEND_TEST(IpTablesTest, ApplyVpnSetupAdd_FailureInUsername); + FRIEND_TEST(IpTablesTest, ApplyVpnSetupAdd_FailureInMasquerade); + FRIEND_TEST(IpTablesTest, ApplyVpnSetupAdd_FailureInRuleForUserTraffic); + FRIEND_TEST(IpTablesTest, ApplyVpnSetupRemove_Success); + FRIEND_TEST(IpTablesTest, ApplyVpnSetupRemove_Failure); + + bool PunchHole(uint16_t port, + const std::string& interface, + std::set<Hole>* holes, + ProtocolEnum protocol); + bool PlugHole(uint16_t port, + const std::string& interface, + std::set<Hole>* holes, + ProtocolEnum protocol); + + bool AddAcceptRules(ProtocolEnum protocol, + uint16_t port, + const std::string& interface); + bool DeleteAcceptRules(ProtocolEnum protocol, + uint16_t port, + const std::string& interface); + + virtual bool AddAcceptRule(const std::string& executable_path, + ProtocolEnum protocol, + uint16_t port, + const std::string& interface); + virtual bool DeleteAcceptRule(const std::string& executable_path, + ProtocolEnum protocol, + uint16_t port, + const std::string& interface); + + bool ApplyVpnSetup(const std::vector<std::string>& usernames, + const std::string& interface, + bool add); + + virtual bool ApplyMasquerade(const std::string& interface, bool add); + bool ApplyMasqueradeWithExecutable(const std::string& interface, + const std::string& executable_path, + bool add); + + virtual bool ApplyMarkForUserTraffic(const std::string& username, bool add); + bool ApplyMarkForUserTrafficWithExecutable(const std::string& username, + const std::string& executable_path, + bool add); + + virtual bool ApplyRuleForUserTraffic(bool add); + bool ApplyRuleForUserTrafficWithVersion(const std::string& ip_version, + bool add); + + int ExecvNonRoot(const std::vector<std::string>& argv, uint64_t capmask); + + // Keep track of firewall holes to avoid adding redundant firewall rules. + std::set<Hole> tcp_holes_; + std::set<Hole> udp_holes_; + + // Tracks whether IPv6 filtering is enabled. If set to |true| (the default), + // then it is required to be working. If |false|, then adding of IPv6 rules is + // still attempted but not mandatory; however, if it is successful even once, + // then it'll be changed to |true| and enforced thereafter. + bool ip6_enabled_ = true; + + DISALLOW_COPY_AND_ASSIGN(IpTables); +}; + +} // namespace firewalld + +#endif // FIREWALLD_IPTABLES_H_
diff --git a/firewalld/iptables_unittest.cc b/firewalld/iptables_unittest.cc new file mode 100644 index 0000000..615b4f8 --- /dev/null +++ b/firewalld/iptables_unittest.cc
@@ -0,0 +1,353 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "iptables.h" + +#include <gtest/gtest.h> + +#include "mock_iptables.h" + +namespace { +#if defined(__ANDROID__) +const char kIpTablesPath[] = "/system/bin/iptables"; +const char kIp6TablesPath[] = "/system/bin/ip6tables"; +#else +const char kIpTablesPath[] = "/sbin/iptables"; +const char kIp6TablesPath[] = "/sbin/ip6tables"; +#endif // __ANDROID__ +} // namespace + +namespace firewalld { + +using testing::_; +using testing::Return; + +class IpTablesTest : public testing::Test { + public: + IpTablesTest() = default; + ~IpTablesTest() override = default; + + protected: + void SetMockExpectations(MockIpTables* iptables, bool success) { + EXPECT_CALL(*iptables, AddAcceptRule(_, _, _, _)) + .WillRepeatedly(Return(success)); + EXPECT_CALL(*iptables, DeleteAcceptRule(_, _, _, _)) + .WillRepeatedly(Return(success)); + } + + void SetMockExpectationsPerExecutable(MockIpTables* iptables, + bool ip4_success, + bool ip6_success) { + EXPECT_CALL(*iptables, AddAcceptRule(kIpTablesPath, _, _, _)) + .WillRepeatedly(Return(ip4_success)); + EXPECT_CALL(*iptables, AddAcceptRule(kIp6TablesPath, _, _, _)) + .WillRepeatedly(Return(ip6_success)); + EXPECT_CALL(*iptables, DeleteAcceptRule(kIpTablesPath, _, _, _)) + .WillRepeatedly(Return(ip4_success)); + EXPECT_CALL(*iptables, DeleteAcceptRule(kIp6TablesPath, _, _, _)) + .WillRepeatedly(Return(ip6_success)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(IpTablesTest); +}; + +TEST_F(IpTablesTest, Port0Fails) { + MockIpTables mock_iptables; + // We should not be adding any rules for port 0. + EXPECT_CALL(mock_iptables, AddAcceptRule(_, _, _, _)).Times(0); + EXPECT_CALL(mock_iptables, DeleteAcceptRule(_, _, _, _)).Times(0); + // Try to punch hole for TCP port 0, port 0 is not a valid port. + EXPECT_FALSE(mock_iptables.PunchTcpHole(0, "iface")); + // Try to punch hole for UDP port 0, port 0 is not a valid port. + EXPECT_FALSE(mock_iptables.PunchUdpHole(0, "iface")); +} + +TEST_F(IpTablesTest, ValidInterfaceName) { + MockIpTables mock_iptables; + SetMockExpectations(&mock_iptables, true /* success */); + + EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "shortname")); + EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "shortname")); + EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "middle-dash")); + EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "middle-dash")); + EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "middle.dot")); + EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "middle.dot")); +} + +TEST_F(IpTablesTest, InvalidInterfaceName) { + MockIpTables mock_iptables; + // We should not be adding any rules for invalid interface names. + EXPECT_CALL(mock_iptables, AddAcceptRule(_, _, _, _)).Times(0); + EXPECT_CALL(mock_iptables, DeleteAcceptRule(_, _, _, _)).Times(0); + + EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "reallylonginterfacename")); + EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "with spaces")); + EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "with$ymbols")); + EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "-startdash")); + EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "enddash-")); + EXPECT_FALSE(mock_iptables.PunchTcpHole(80, ".startdot")); + EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "enddot.")); + + EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "reallylonginterfacename")); + EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "with spaces")); + EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "with$ymbols")); + EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "-startdash")); + EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "enddash-")); + EXPECT_FALSE(mock_iptables.PunchUdpHole(53, ".startdot")); + EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "enddot.")); +} + +TEST_F(IpTablesTest, PunchTcpHoleSucceeds) { + MockIpTables mock_iptables; + SetMockExpectations(&mock_iptables, true /* success */); + + // Punch hole for TCP port 80, should succeed. + EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "iface")); + // Punch again, should still succeed. + EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "iface")); + // Plug the hole, should succeed. + EXPECT_TRUE(mock_iptables.PlugTcpHole(80, "iface")); +} + +TEST_F(IpTablesTest, PlugTcpHoleSucceeds) { + MockIpTables mock_iptables; + SetMockExpectations(&mock_iptables, true /* success */); + + // Punch hole for TCP port 80, should succeed. + EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "iface")); + // Plug the hole, should succeed. + EXPECT_TRUE(mock_iptables.PlugTcpHole(80, "iface")); + // Plug again, should fail. + EXPECT_FALSE(mock_iptables.PlugTcpHole(80, "iface")); +} + +TEST_F(IpTablesTest, PunchUdpHoleSucceeds) { + MockIpTables mock_iptables; + SetMockExpectations(&mock_iptables, true /* success */); + + // Punch hole for UDP port 53, should succeed. + EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "iface")); + // Punch again, should still succeed. + EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "iface")); + // Plug the hole, should succeed. + EXPECT_TRUE(mock_iptables.PlugUdpHole(53, "iface")); +} + +TEST_F(IpTablesTest, PlugUdpHoleSucceeds) { + MockIpTables mock_iptables; + SetMockExpectations(&mock_iptables, true /* success */); + + // Punch hole for UDP port 53, should succeed. + EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "iface")); + // Plug the hole, should succeed. + EXPECT_TRUE(mock_iptables.PlugUdpHole(53, "iface")); + // Plug again, should fail. + EXPECT_FALSE(mock_iptables.PlugUdpHole(53, "iface")); +} + +TEST_F(IpTablesTest, PunchTcpHoleFails) { + MockIpTables mock_iptables; + SetMockExpectations(&mock_iptables, false /* success */); + // Punch hole for TCP port 80, should fail. + ASSERT_FALSE(mock_iptables.PunchTcpHole(80, "iface")); +} + +TEST_F(IpTablesTest, PunchUdpHoleFails) { + MockIpTables mock_iptables; + SetMockExpectations(&mock_iptables, false /* success */); + // Punch hole for UDP port 53, should fail. + ASSERT_FALSE(mock_iptables.PunchUdpHole(53, "iface")); +} + +TEST_F(IpTablesTest, PunchTcpHoleIpv6Fails) { + MockIpTables mock_iptables; + SetMockExpectationsPerExecutable( + &mock_iptables, true /* ip4_success */, false /* ip6_success */); + // Punch hole for TCP port 80, should fail because 'ip6tables' fails. + ASSERT_FALSE(mock_iptables.PunchTcpHole(80, "iface")); +} + +TEST_F(IpTablesTest, PunchUdpHoleIpv6Fails) { + MockIpTables mock_iptables; + SetMockExpectationsPerExecutable( + &mock_iptables, true /* ip4_success */, false /* ip6_success */); + // Punch hole for UDP port 53, should fail because 'ip6tables' fails. + ASSERT_FALSE(mock_iptables.PunchUdpHole(53, "iface")); +} + +TEST_F(IpTablesTest, ApplyVpnSetupAdd_Success) { + const std::vector<std::string> usernames = {"testuser0", "testuser1"}; + const std::string interface = "ifc0"; + const bool add = true; + + MockIpTables mock_iptables; + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add)) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(usernames[0], add)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(usernames[1], add)) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)) + .WillOnce(Return(true)); + + ASSERT_TRUE( + mock_iptables.ApplyVpnSetup(usernames, interface, add)); +} + +TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInUsername) { + const std::vector<std::string> usernames = {"testuser0", "testuser1"}; + const std::string interface = "ifc0"; + const bool remove = false; + const bool add = true; + + MockIpTables mock_iptables; + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add)) + .Times(1) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, + ApplyMarkForUserTraffic(usernames[0], add)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(mock_iptables, + ApplyMarkForUserTraffic(usernames[1], add)) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)) + .Times(1) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove)) + .Times(1) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, + ApplyMarkForUserTraffic(usernames[0], remove)) + .Times(1) + .WillOnce(Return(false)); + EXPECT_CALL(mock_iptables, + ApplyMarkForUserTraffic(usernames[1], remove)) + .Times(0); + + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove)) + .Times(1) + .WillOnce(Return(false)); + + ASSERT_FALSE( + mock_iptables.ApplyVpnSetup(usernames, interface, add)); +} + +TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInMasquerade) { + const std::vector<std::string> usernames = {"testuser0", "testuser1"}; + const std::string interface = "ifc0"; + const bool remove = false; + const bool add = true; + + MockIpTables mock_iptables; + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add)) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, _)).Times(0); + + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)) + .Times(1) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove)) + .Times(1) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove)) + .Times(1) + .WillOnce(Return(true)); + + ASSERT_FALSE( + mock_iptables.ApplyVpnSetup(usernames, interface, add)); +} + +TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInRuleForUserTraffic) { + const std::vector<std::string> usernames = {"testuser0", "testuser1"}; + const std::string interface = "ifc0"; + const bool remove = false; + const bool add = true; + + MockIpTables mock_iptables; + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, _)).Times(0); + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, _)).Times(0); + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove)).Times(1); + + ASSERT_FALSE(mock_iptables.ApplyVpnSetup(usernames, interface, add)); +} + +TEST_F(IpTablesTest, ApplyVpnSetupRemove_Success) { + const std::vector<std::string> usernames = {"testuser0", "testuser1"}; + const std::string interface = "ifc0"; + const bool remove = false; + const bool add = true; + + MockIpTables mock_iptables; + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, remove)) + .Times(2) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove)) + .Times(1) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add)).Times(0); + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, add)).Times(0); + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)).Times(0); + + ASSERT_TRUE(mock_iptables.ApplyVpnSetup(usernames, interface, remove)); +} + +TEST_F(IpTablesTest, ApplyVpnSetupRemove_Failure) { + const std::vector<std::string> usernames = {"testuser0", "testuser1"}; + const std::string interface = "ifc0"; + const bool remove = false; + const bool add = true; + + MockIpTables mock_iptables; + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove)) + .Times(1) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, remove)) + .Times(2) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove)) + .Times(1) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add)).Times(0); + + EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, add)).Times(0); + EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)).Times(0); + + ASSERT_FALSE(mock_iptables.ApplyVpnSetup(usernames, interface, remove)); +} + +} // namespace firewalld
diff --git a/firewalld/libfirewalld-client-test.pc.in b/firewalld/libfirewalld-client-test.pc.in new file mode 100644 index 0000000..a075a16 --- /dev/null +++ b/firewalld/libfirewalld-client-test.pc.in
@@ -0,0 +1,6 @@ +include_dir=@INCLUDE_DIR@ + +Name: libfirewalld-client-test +Version: 1.0 +Description: firewalld client interface mock library +Cflags: -I${include_dir}
diff --git a/firewalld/libfirewalld-client.pc.in b/firewalld/libfirewalld-client.pc.in new file mode 100644 index 0000000..79c7d78 --- /dev/null +++ b/firewalld/libfirewalld-client.pc.in
@@ -0,0 +1,6 @@ +include_dir=@INCLUDE_DIR@ + +Name: libfirewalld-client +Version: 1.0 +Description: firewalld client interface library +Cflags: -I${include_dir}
diff --git a/firewalld/main.cc b/firewalld/main.cc new file mode 100644 index 0000000..76e8dd5 --- /dev/null +++ b/firewalld/main.cc
@@ -0,0 +1,28 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <base/command_line.h> +#include <brillo/syslog_logging.h> + +#include "firewall_daemon.h" + +using firewalld::FirewallDaemon; + +int main(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + brillo::InitLog(brillo::kLogToSyslog); + + FirewallDaemon daemon; + return daemon.Run(); +}
diff --git a/firewalld/mock_iptables.cc b/firewalld/mock_iptables.cc new file mode 100644 index 0000000..767637f --- /dev/null +++ b/firewalld/mock_iptables.cc
@@ -0,0 +1,23 @@ +// Copyright 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "mock_iptables.h" + +namespace firewalld { + +MockIpTables::~MockIpTables() { + PlugAllHoles(); +} + +} // namespace firewalld
diff --git a/firewalld/mock_iptables.h b/firewalld/mock_iptables.h new file mode 100644 index 0000000..54aaa25 --- /dev/null +++ b/firewalld/mock_iptables.h
@@ -0,0 +1,50 @@ +// Copyright 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREWALLD_MOCK_IPTABLES_H_ +#define FIREWALLD_MOCK_IPTABLES_H_ + +#include <string> + +#include <base/macros.h> +#include <gmock/gmock.h> + +#include "iptables.h" + +namespace firewalld { + +class MockIpTables : public IpTables { + public: + MockIpTables() = default; + ~MockIpTables() override; + + MOCK_METHOD4( + AddAcceptRule, + bool(const std::string&, ProtocolEnum, uint16_t, const std::string&)); + + MOCK_METHOD4( + DeleteAcceptRule, + bool(const std::string&, ProtocolEnum, uint16_t, const std::string&)); + + MOCK_METHOD2(ApplyMasquerade, bool(const std::string&, bool)); + MOCK_METHOD2(ApplyMarkForUserTraffic, bool(const std::string&, bool)); + MOCK_METHOD1(ApplyRuleForUserTraffic, bool(bool)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockIpTables); +}; + +} // namespace firewalld + +#endif // FIREWALLD_MOCK_IPTABLES_H_
diff --git a/firewalld/run_all_tests.cc b/firewalld/run_all_tests.cc new file mode 100644 index 0000000..b3137cf --- /dev/null +++ b/firewalld/run_all_tests.cc
@@ -0,0 +1,20 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <gtest/gtest.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}