main.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2024-2026 Marek Küthe <m.k@mk16.de> 2 // 3 // SPDX-License-Identifier: GPL-3.0-or-later 4 5 #include <fstream> 6 #include <memory> 7 #include <span> 8 #include <sstream> 9 #include <system_error> 10 #include <stdexcept> 11 #include <cstdlib> 12 #include <boost/asio.hpp> 13 #include <boost/log/trivial.hpp> 14 #include <boost/version.hpp> 15 #include "capability_managment.hpp" 16 #include "capsicum.hpp" 17 #include "configuration.hpp" 18 #include "crazytrace.hpp" 19 #include "landlock.hpp" 20 #include "nodecontainer.hpp" 21 #include "posix_wrapper.hpp" 22 #include "seccomp.hpp" 23 #include <tuntap++.hh> 24 25 int main(int argc, char * argv[]) // NOLINT(bugprone-exception-escape) 26 { 27 try 28 { 29 #ifdef HAVE_LIBCAPNG 30 CapabilityManagment::check_for_capabilites(); 31 CapabilityManagment::lock(); 32 CapabilityManagment::drop_capabilies(); 33 #endif 34 #ifdef HAVE_LANDLOCK 35 const LandlockRuleset landlock_ruleset_init( 36 LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_REMOVE_DIR | 37 LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_CHAR | 38 LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | 39 LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | 40 LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM | 41 LANDLOCK_ACCESS_FS_REFER, 42 LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP, 43 LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 44 #ifdef HAVE_LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON 45 landlock_ruleset_init.restrict_self( 46 LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON); 47 #else 48 landlock_ruleset_init.restrict_self(); 49 #endif 50 #endif 51 52 #ifdef HAVE_SECCOMP 53 SeccompFilterContext seccomp_context(SCMP_ACT_ALLOW); 54 // see also 55 // https://lists.boost.org/archives/list/boost-users@lists.boost.org/thread/YJ5RTK25HLPFEZ3XVBBFQDJOSPIIOBNA/ 56 // and https://sourceforge.net/p/asio/mailman/message/59260797/ 57 // due to complexity use seccomp blacklist 58 seccomp_context.kill_chown(); 59 seccomp_context.kill_clock(); 60 seccomp_context.kill_cpu_emulation(); 61 seccomp_context.kill_debug(); 62 seccomp_context.kill_others(); 63 seccomp_context.kill_ipc(); 64 seccomp_context.kill_keyring(); 65 seccomp_context.kill_memlock(); 66 seccomp_context.kill_module(); 67 seccomp_context.kill_obsolete(); 68 seccomp_context.kill_privileged(); 69 seccomp_context.kill_rawio(); 70 seccomp_context.kill_reboot(); 71 seccomp_context.kill_resources(); 72 #ifndef HAVE_SETUGID 73 seccomp_context.kill_setuid(); 74 #endif 75 seccomp_context.kill_swap(); 76 seccomp_context.kill_sync(); 77 seccomp_context.kill_system_service(); 78 seccomp_context.load(); 79 #endif 80 81 const auto args = std::span(argv, static_cast<std::size_t>(argc)); 82 if (args.size() != 2) 83 throw std::runtime_error("A configuration file must be specified."); 84 85 const std::string filename(args.at(1)); 86 const crazytrace::Configuration config(filename); 87 config.get_log_level().apply(); 88 89 BOOST_LOG_TRIVIAL(info) 90 << "libtuntap version (compile time): " << TUNTAP_VERSION_MAJOR 91 << "." << TUNTAP_VERSION_MINOR; 92 const int version = ::tuntap_version(); 93 const int major = (version >> 8) & 0xFF; 94 const int minor = version & 0xFF; 95 BOOST_LOG_TRIVIAL(info) 96 << "libtuntap version (runtime): " << major << "." << minor; 97 98 #if defined(TINS_VERSION_MAJOR) && defined(TINS_VERSION_MINOR) && \ 99 defined(TINS_VERSION_PATCH) 100 BOOST_LOG_TRIVIAL(info) 101 << "libtins version (compile time): " << TINS_VERSION_MAJOR << "." 102 << TINS_VERSION_MINOR << "." << TINS_VERSION_PATCH; 103 #endif 104 105 BOOST_LOG_TRIVIAL(info) 106 << "Boost version (compile time): " << (BOOST_VERSION / 100'000) 107 << "." << (BOOST_VERSION / 100 % 1000) << "." 108 << (BOOST_VERSION % 100); 109 110 #ifdef HAVE_LIBCAPNG 111 BOOST_LOG_TRIVIAL(info) << "libcapng: true"; 112 #else 113 BOOST_LOG_TRIVIAL(info) << "libcapng: false"; 114 #endif 115 116 #ifdef HAVE_SECCOMP 117 BOOST_LOG_TRIVIAL(info) << "seccomp: true"; 118 const auto * seccomp_ver = seccomp_version(); 119 BOOST_LOG_TRIVIAL(info) 120 << "seccomp version (runtime): " << seccomp_ver->major << "." 121 << seccomp_ver->minor << "." << seccomp_ver->micro; 122 #else 123 BOOST_LOG_TRIVIAL(info) << "seccomp: false"; 124 #endif 125 126 #ifdef HAVE_LANDLOCK 127 BOOST_LOG_TRIVIAL(info) << "Landlock: true"; 128 BOOST_LOG_TRIVIAL(info) 129 << "Landlock ABI version: " << LandlockRuleset::get_abi_version(); 130 #else 131 BOOST_LOG_TRIVIAL(info) << "Landlock: false"; 132 #endif 133 134 #ifdef HAVE_CAPSICUM 135 BOOST_LOG_TRIVIAL(info) << "capsicum: true"; 136 #else 137 BOOST_LOG_TRIVIAL(info) << "capsicum: false"; 138 #endif 139 140 #ifdef HAVE_SETUGID 141 BOOST_LOG_TRIVIAL(info) << "setugid: true"; 142 #else 143 BOOST_LOG_TRIVIAL(info) << "setugid: false"; 144 #endif 145 146 const std::shared_ptr<crazytrace::NodeContainer> nodecontainer = 147 config.get_node_container(); 148 149 std::ostringstream nodes_verbose; 150 nodecontainer->print(nodes_verbose); 151 BOOST_LOG_TRIVIAL(info) << nodes_verbose.str(); 152 153 constexpr std::size_t mtu = 1500; 154 BOOST_LOG_TRIVIAL(debug) << "Create TUN device."; 155 tuntap::tuntap dev(TUNTAP_MODE_ETHERNET); 156 dev.name(config.get_device_name()); 157 BOOST_LOG_TRIVIAL(debug) << "Set MTU to " << mtu << "."; 158 dev.mtu(mtu); 159 BOOST_LOG_TRIVIAL(debug) << "Set the TUN device up."; 160 dev.up(); 161 162 const int tap_dev_fd = PosixWrapper::dup(dev.native_handle()); 163 164 boost::asio::io_context io; 165 #ifdef BOOST_PROCESS_V1 166 config.get_postup_commands().execute_commands(); 167 #else 168 config.get_postup_commands().execute_commands(io.get_executor()); 169 #endif 170 171 #ifdef HAVE_SETUGID 172 if (config.has_setugid()) 173 { 174 const std::string& username = config.get_user(); 175 const auto uid = PosixWrapper::username_to_uid(username); 176 PosixWrapper::set_uid(uid); 177 BOOST_LOG_TRIVIAL(info) 178 << "setuid: " << username << " (" << uid << ")"; 179 180 const std::string& groupname = config.get_group(); 181 const auto gid = PosixWrapper::groupname_to_gid(groupname); 182 PosixWrapper::set_gid(gid); 183 BOOST_LOG_TRIVIAL(info) 184 << "setgid: " << groupname << " (" << gid << ")"; 185 } 186 #endif 187 #ifdef HAVE_LIBCAPNG 188 CapabilityManagment::drop_all_capabilies(); 189 #endif 190 #ifdef HAVE_LANDLOCK 191 const LandlockRuleset landlock_ruleset_loop( 192 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_WRITE_FILE | 193 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_TRUNCATE | 194 LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_REMOVE_DIR | 195 LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_CHAR | 196 LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | 197 LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | 198 LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM | 199 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_IOCTL_DEV, 200 LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP, 201 LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL); 202 // see also 203 // https://lore.kernel.org/landlock/20251119212707.71275873@ciel/T/ 204 #ifdef HAVE_LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON 205 landlock_ruleset_init.restrict_self( 206 LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON); 207 #else 208 landlock_ruleset_init.restrict_self(); 209 #endif 210 #endif 211 #ifdef HAVE_SECCOMP 212 seccomp_context.kill_signal(); 213 seccomp_context.load(); 214 seccomp_context.release(); 215 #endif 216 #ifdef HAVE_CAPSICUM 217 Capsicum::enter(); 218 Capsicum::limit_stdio(); 219 Capsicum::limit_rights(dev.native_handle(), 220 CAP_EVENT, 221 CAP_FCNTL, 222 CAP_IOCTL, 223 CAP_READ, 224 CAP_WRITE); 225 Capsicum::limit_fcntls(dev.native_handle(), 226 CAP_FCNTL_GETFL | CAP_FCNTL_SETFL); 227 Capsicum::limit_ioctls(dev.native_handle(), 228 {FIONBIO, FIONREAD, SIOCATMARK}); 229 230 if (Capsicum::in_capability_mode()) 231 { 232 BOOST_LOG_TRIVIAL(info) << "capsicum capabiliy mode: true"; 233 } 234 else 235 { 236 BOOST_LOG_TRIVIAL(info) << "capsicum capabiliy mode: false"; 237 } 238 #endif 239 240 const crazytrace::Crazytrace ct( 241 io.get_executor(), tap_dev_fd, nodecontainer); 242 243 io.run(); 244 } 245 catch (const std::exception& e) 246 { 247 BOOST_LOG_TRIVIAL(fatal) << "Error: " << e.what() << std::endl 248 << "Exit program."; 249 std::exit(EXIT_FAILURE); // NOLINT(concurrency-mt-unsafe) 250 } 251 catch (...) 252 { 253 BOOST_LOG_TRIVIAL(fatal) << "Unknown error caught." << std::endl 254 << "Exit program."; 255 std::exit(EXIT_FAILURE); // NOLINT(concurrency-mt-unsafe) 256 } 257 return EXIT_SUCCESS; 258 }