/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "client/AdminClient.h" #include "admin/angel/Core.h" #include "admin/angel/InterfaceWaiter.h" #include "client/Configurator.h" #include "benc/Dict.h" #include "benc/Int.h" #include "benc/List.h" #include "benc/serialization/BencSerializer.h" #include "benc/serialization/json/JsonBencSerializer.h" #include "benc/serialization/standard/BencMessageReader.h" #include "benc/serialization/standard/BencMessageWriter.h" #include "crypto/AddressCalc.h" #include "crypto/CryptoAuth.h" #include "dht/Address.h" #include "exception/Except.h" #include "interface/Iface.h" #include "io/FileReader.h" #include "io/FileWriter.h" #include "io/Reader.h" #include "io/Writer.h" #include "memory/Allocator.h" #include "memory/MallocAllocator.h" #include "util/ArchInfo.h" #include "util/Assert.h" #include "util/Base32.h" #include "util/CString.h" #include "util/events/UDPAddrIface.h" #include "util/events/Time.h" #include "util/events/EventBase.h" #include "util/events/Pipe.h" #include "util/events/Process.h" #include "util/Hex.h" #include "util/log/Log.h" #include "util/log/FileWriterLog.h" #include "util/SysInfo.h" #include "util/version/Version.h" #include "net/Benchmark.h" #include "crypto_scalarmult_curve25519.h" #include #include #include #define DEFAULT_TUN_DEV "tun0" static int genAddress(uint8_t addressOut[40], uint8_t privateKeyHexOut[65], uint8_t publicKeyBase32Out[53], struct Random* rand) { struct Address address; uint8_t privateKey[32]; for (;;) { Random_bytes(rand, privateKey, 32); crypto_scalarmult_curve25519_base(address.key, privateKey); // Brute force for keys until one matches FC00:/8 if (AddressCalc_addressForPublicKey(address.ip6.bytes, address.key)) { Hex_encode(privateKeyHexOut, 65, privateKey, 32); Base32_encode(publicKeyBase32Out, 53, address.key, 32); Address_printShortIp(addressOut, &address); return 0; } } } static int genconf(struct Random* rand, bool eth) { uint8_t password[32]; uint8_t password2[32]; uint8_t password3[32]; uint8_t password4[32]; Random_base32(rand, password, 32); Random_base32(rand, password2, 32); Random_base32(rand, password3, 32); Random_base32(rand, password4, 32); uint8_t adminPassword[32]; Random_base32(rand, adminPassword, 32); uint16_t port = 0; while (port <= 1024) { port = Random_uint16(rand); } uint8_t publicKeyBase32[53]; uint8_t address[40]; uint8_t privateKeyHex[65]; genAddress(address, privateKeyHex, publicKeyBase32, rand); printf("{\n"); printf(" // Private key:\n" " // Your confidentiality and data integrity depend on this key, keep it secret!\n" " \"privateKey\": \"%s\",\n\n", privateKeyHex); printf(" // This key corresponds to the public key and ipv6 address:\n" " \"publicKey\": \"%s.k\",\n", publicKeyBase32); printf(" \"ipv6\": \"%s\",\n", address); printf("\n" " // Anyone connecting and offering these passwords on connection will be allowed.\n" " //\n" " // WARNING: Currently there is no key derivation done on the password field,\n" " // DO NOT USE A PASSWORD HERE use something which is truly random and\n" " // cannot be guessed.\n" " // Including a username in the beginning of the password string is encouraged\n" " // to aid in remembering which users are who.\n" " //\n" " \"authorizedPasswords\":\n" " [\n" " // A unique string which is known to the client and server.\n" " {\"password\": \"%s\"}\n", password); printf("\n" " // More passwords should look like this.\n" " // {\"password\": \"%s\"},\n", password2); printf(" // {\"password\": \"%s\"},\n", password3); printf(" // {\"password\": \"%s\"},\n", password4); printf("\n" " // Below is an example of your connection credentials\n" " // that you can give to other people so they can connect\n" " // to you using your default password (from above)\n" " // Adding a unique password for each user is advisable\n" " // so that leaks can be isolated.\n" " //\n" " // \"your.external.ip.goes.here:%u\":{", port); printf("\"password\":\"%s\",", password); printf("\"publicKey\":\"%s.k\"}\n", publicKeyBase32); printf(" ],\n" "\n" " // Settings for administering and extracting information from your router.\n" " // This interface provides functions which can be called through a UDP socket.\n" " // See admin/Readme.md for more information about the API and try:\n" " // ./contrib/python/cexec 'functions'\n" " // For a list of functions which can be called.\n" " // For example: ./contrib/python/cexec 'memory()'\n" " // will call a function which gets the core's current memory consumption.\n" " // ./contrib/python/cjdnslog\n" " // is a tool which uses this admin interface to get logs from cjdns.\n" " \"admin\":\n" " {\n" " // Port to bind the admin RPC server to.\n" " \"bind\": \"127.0.0.1:11234\",\n" "\n" " // Password for admin RPC server.\n" " \"password\": \"%s\"\n", adminPassword); printf(" },\n" "\n" " // Interfaces to connect to the switch core.\n" " \"interfaces\":\n" " {\n" " // The interface which connects over UDP/IP based VPN tunnel.\n" " \"UDPInterface\":\n" " [\n" " {\n" " // Bind to this port.\n" " \"bind\": \"0.0.0.0:%u\",\n", port); printf("\n" " // Nodes to connect to (IPv4 only).\n" " \"connectTo\":\n" " {\n" " // Add connection credentials here to join the network\n" " // Ask somebody who is already connected.\n" " }\n" " },\n" " {\n" " // Bind to this port.\n" " \"bind\": \"[::]:%u\",\n", port); printf("\n" " // Nodes to connect to (IPv6 only).\n" " \"connectTo\":\n" " {\n" " // Add connection credentials here to join the network\n" " // Ask somebody who is already connected.\n" " }\n" " }\n" " ]\n"); #ifdef HAS_ETH_INTERFACE printf("\n"); if (!eth) { printf(" /*\n"); } printf(" \"ETHInterface\":\n" " [\n" " // Alternatively bind to just one device and either beacon and/or\n" " // connect to a specified MAC address\n" " {\n" " // Bind to this device (interface name, not MAC)\n" " // \"all\" is a pseudo-name which will try to connect to all devices.\n" " \"bind\": \"all\",\n" "\n" " // Auto-connect to other cjdns nodes on the same network.\n" " // Options:\n" " //\n" " // 0 -- Disabled.\n" " //\n" " // 1 -- Accept beacons, this will cause cjdns to accept incoming\n" " // beacon messages and try connecting to the sender.\n" " //\n" " // 2 -- Accept and send beacons, this will cause cjdns to broadcast\n" " // messages on the local network which contain a randomly\n" " // generated per-session password, other nodes which have this\n" " // set to 1 or 2 will hear the beacon messages and connect\n" " // automatically.\n" " //\n" " \"beacon\": 2,\n" "\n" " // Node(s) to connect to manually\n" " // Note: does not work with \"all\" pseudo-device-name\n" " \"connectTo\":\n" " {\n" " // Credentials for connecting look similar to UDP credientials\n" " // except they begin with the mac address, for example:\n" " // \"01:02:03:04:05:06\":{\"password\":\"a\",\"publicKey\":\"b\"}\n" " }\n" " }\n" " ]\n"); if (!eth) { printf(" */\n"); } printf("\n"); #endif printf(" },\n" "\n" " // Configuration for the router.\n" " \"router\":\n" " {\n" " // The interface which is used for connecting to the cjdns network.\n" " \"interface\":\n" " {\n" " // The type of interface (only TUNInterface is supported for now)\n" " \"type\": \"TUNInterface\"\n" #ifndef __APPLE__ "\n" " // The name of a persistent TUN device to use.\n" " // This for starting cjdroute as its own user.\n" " // *MOST USERS DON'T NEED THIS*\n" " //\"tunDevice\": \"" DEFAULT_TUN_DEV "\"\n" #endif " },\n" "\n" " // System for tunneling IPv4 and ICANN IPv6 through cjdns.\n" " // This is using the cjdns switch layer as a VPN carrier.\n" " \"ipTunnel\":\n" " {\n" " // Nodes allowed to connect to us.\n" " // When a node with the given public key connects, give them the\n" " // ip4 and/or ip6 addresses listed.\n" " \"allowedConnections\":\n" " [\n" " // Give the client an address on 192.168.1.0/24, and an address\n" " // it thinks has all of IPv6 behind it.\n" " // {\n" " // \"publicKey\": " "\"f64hfl7c4uxt6krmhPutTheRealAddressOfANodeHere7kfm5m0.k\",\n" " // \"ip4Address\": \"192.168.1.24\",\n" " // \"ip4Prefix\": 24,\n" " // \"ip6Address\": \"2001:123:ab::10\",\n" " // \"ip6Prefix\": 0\n" " // },\n" "\n" " // It's ok to only specify one address.\n" " // {\n" " // \"publicKey\": " "\"ydq8csdk8p8ThisIsJustAnExampleAddresstxuyqdf27hvn2z0.k\",\n" " // \"ip4Address\": \"192.168.1.25\",\n" " // \"ip4Prefix\": 24\n" " // }\n" " ],\n" "\n" " \"outgoingConnections\":\n" " [\n" " // Connect to one or more machines and ask them for IP addresses.\n" " // \"6743gf5tw80ExampleExampleExampleExamplevlyb23zfnuzv0.k\",\n" " // \"pw9tfmr8pcrExampleExampleExampleExample8rhg1pgwpwf80.k\",\n" " // \"g91lxyxhq0kExampleExampleExampleExample6t0mknuhw75l0.k\"\n" " ]\n" " }\n" " },\n" "\n"); printf(" // Dropping permissions.\n" " // In the event of a serious security exploit in cjdns, leak of confidential\n" " // network traffic and/or keys is highly likely but the following rules are\n" " // designed to prevent the attack from spreading to the system on which cjdns\n" " // is running.\n" " // Counter-intuitively, cjdns is *more* secure if it is started as root because\n" " // non-root users do not have permission to use chroot or change usernames,\n" " // limiting the effectiveness of the mitigations herein.\n" " \"security\":\n" " [\n" " // Change the user id to sandbox the cjdns process after it starts.\n" " // If keepNetAdmin is set to 0, IPTunnel will be unable to set IP addresses\n" " // and ETHInterface will be unable to hot-add new interfaces\n" " // Use { \"setuser\": 0 } to disable.\n" " // Default: enabled with keepNetAdmin\n" " { \"setuser\": \"nobody\", \"keepNetAdmin\": 1 },\n" "\n" " // Chroot changes the filesystem root directory which cjdns sees, blocking it\n" " // from accessing files outside of the chroot sandbox, if the user does not\n" " // have permission to use chroot(), this will fail quietly.\n" " // Use { \"chroot\": 0 } to disable.\n" " // Default: enabled (using \"/var/run\")\n" " { \"chroot\": \"/var/run/\" },\n" "\n" " // Nofiles is a deprecated security feature which prevents cjdns from opening\n" " // any files at all, using this will block setting of IP addresses and\n" " // hot-adding ETHInterface devices but for users who do not need this, it\n" " // provides a formidable sandbox.\n" " // Default: disabled\n" " { \"nofiles\": 0 },\n" "\n" " // Noforks will prevent cjdns from spawning any new processes or threads,\n" " // this prevents many types of exploits from attacking the wider system.\n" " // Default: enabled\n" " { \"noforks\": 1 },\n" "\n" " // Seccomp is the most advanced sandboxing feature in cjdns, it uses\n" " // SECCOMP_BPF to filter the system calls which cjdns is able to make on a\n" " // linux system, strictly limiting it's access to the outside world\n" " // This will fail quietly on any non-linux system\n" " // Default: enabled\n" " { \"seccomp\": 1 },\n" "\n" " // The client sets up the core using a sequence of RPC calls, the responses\n" " // to these calls are verified but in the event that the client crashes\n" " // setup of the core completes, it could leave the core in an insecure state\n" " // This call constitutes the client telling the core that the security rules\n" " // have been fully applied and the core may run. Without it, the core will\n" " // exit within a few seconds with return code 232.\n" " // Default: enabled\n" " { \"setupComplete\": 1 }\n" " ],\n" "\n" " // Logging\n" " \"logging\":\n" " {\n" " // Uncomment to have cjdns log to stdout rather than making logs available\n" " // via the admin socket.\n" " // \"logTo\":\"stdout\"\n" " },\n" "\n" " // If set to non-zero, cjdns will not fork to the background.\n" " // Recommended for use in conjunction with \"logTo\":\"stdout\".\n" " \"noBackground\":0,\n" "}\n"); return 0; } static int usage(struct Allocator* alloc, char* appName) { char* sysInfo = SysInfo_describe(SysInfo_detect(), alloc); printf("Cjdns %s %s\n" "Usage: %s [--help] [--genconf] [--bench] [--version] [--cleanconf] [--nobg]\n" "\n" "To get the router up and running.\n" "Step 1:\n" " Generate a new configuration file.\n" " %s --genconf > cjdroute.conf\n" "\n" "Step 2:\n" " Find somebody to connect to.\n" " Check out the IRC channel or http://hyperboria.net/\n" " for information about how to meet new people and make connect to them.\n" " Read more here: https://github.com/cjdelisle/cjdns/#2-find-a-friend\n" "\n" "Step 3:\n" " Add that somebody's node to your cjdroute.conf file.\n" " https://github.com/cjdelisle/cjdns/#3-connect-your-node-to-your-friends-node\n" "\n" "Step 4:\n" " Fire it up!\n" " sudo %s < cjdroute.conf\n" "\n" "For more information about other functions and non-standard setups, see README.md\n", ArchInfo_getArchStr(), sysInfo, appName, appName, appName); return 0; } struct CheckRunningInstanceContext { struct EventBase* base; struct Allocator* alloc; struct AdminClient_Result* res; }; static void checkRunningInstanceCallback(struct AdminClient_Promise* p, struct AdminClient_Result* res) { struct CheckRunningInstanceContext* ctx = p->userData; // Prevent this from freeing until after we drop out of the loop. Allocator_adopt(ctx->alloc, p->alloc); ctx->res = res; EventBase_endLoop(ctx->base); } static void checkRunningInstance(struct Allocator* allocator, struct EventBase* base, String* addr, String* password, struct Log* logger, struct Except* eh) { struct Allocator* alloc = Allocator_child(allocator); struct Sockaddr_storage pingAddrStorage; if (Sockaddr_parse(addr->bytes, &pingAddrStorage)) { Except_throw(eh, "Unable to parse [%s] as an ip address port, eg: 127.0.0.1:11234", addr->bytes); } struct UDPAddrIface* udp = UDPAddrIface_new(base, NULL, alloc, NULL, logger); struct AdminClient* adminClient = AdminClient_new(&udp->generic, &pingAddrStorage.addr, password, base, logger, alloc); // 100 milliseconds is plenty to wait for a process to respond on the same machine. adminClient->millisecondsToWait = 100; Dict* pingArgs = Dict_new(alloc); struct AdminClient_Promise* pingPromise = AdminClient_rpcCall(String_new("ping", alloc), pingArgs, adminClient, alloc); struct CheckRunningInstanceContext* ctx = Allocator_malloc(alloc, sizeof(struct CheckRunningInstanceContext)); ctx->base = base; ctx->alloc = alloc; ctx->res = NULL; pingPromise->callback = checkRunningInstanceCallback; pingPromise->userData = ctx; EventBase_beginLoop(base); Assert_true(ctx->res); if (ctx->res->err != AdminClient_Error_TIMEOUT) { Except_throw(eh, "Startup failed: cjdroute is already running. [%d]", ctx->res->err); } Allocator_free(alloc); } static void onCoreExit(int64_t exit_status, int term_signal) { Assert_failure("Core exited with status [%d], signal [%d]\n", (int)exit_status, term_signal); } int main(int argc, char** argv) { #ifdef Log_KEYS fprintf(stderr, "Log_LEVEL = KEYS, EXPECT TO SEE PRIVATE KEYS IN YOUR LOGS!\n"); #endif if (argc > 1 && (!CString_strcmp("angel", argv[1]) || !CString_strcmp("core", argv[1]))) { return Core_main(argc, argv); } Assert_ifParanoid(argc > 0); struct Except* eh = NULL; // Allow it to allocate 8MB struct Allocator* allocator = MallocAllocator_new(1<<23); struct Random* rand = Random_new(allocator, NULL, eh); struct EventBase* eventBase = EventBase_new(allocator); if (argc >= 2) { // one argument if ((CString_strcmp(argv[1], "--help") == 0) || (CString_strcmp(argv[1], "-h") == 0)) { return usage(allocator, argv[0]); } else if (CString_strcmp(argv[1], "--genconf") == 0) { bool eth = 0; for (int i = 1; i < argc; i++) { if (!CString_strcmp(argv[i], "--eth")) { eth = 1; } } return genconf(rand, eth); } else if (CString_strcmp(argv[1], "--pidfile") == 0) { // deprecated fprintf(stderr, "'--pidfile' option is deprecated.\n"); return 0; } else if (CString_strcmp(argv[1], "--reconf") == 0) { // Performed after reading the configuration } else if (CString_strcmp(argv[1], "--bench") == 0) { Benchmark_runAll(); return 0; } else if ((CString_strcmp(argv[1], "--version") == 0) || (CString_strcmp(argv[1], "-v") == 0)) { printf("Cjdns protocol version: %d\n", Version_CURRENT_PROTOCOL); return 0; } else if (CString_strcmp(argv[1], "--cleanconf") == 0) { // Performed after reading configuration } else if (CString_strcmp(argv[1], "--nobg") == 0) { // Performed while reading configuration } else { fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[1]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); return -1; } } else if (argc > 2) { // more than one argument? fprintf(stderr, "%s: too many arguments [%s]\n", argv[0], argv[1]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); // because of '--pidfile $filename'? if (CString_strcmp(argv[1], "--pidfile") == 0) { fprintf(stderr, "\n'--pidfile' option is deprecated.\n"); } return -1; } if (isatty(STDIN_FILENO)) { // We were started from a terminal // The chances an user wants to type in a configuration // bij hand are pretty slim so we show him the usage return usage(allocator, argv[0]); } else { // We assume stdin is a configuration file and that we should // start routing } struct Reader* stdinReader = FileReader_new(stdin, allocator); Dict config; if (JsonBencSerializer_get()->parseDictionary(stdinReader, allocator, &config)) { fprintf(stderr, "Failed to parse configuration.\n"); return -1; } if (argc == 2 && CString_strcmp(argv[1], "--cleanconf") == 0) { struct Writer* stdoutWriter = FileWriter_new(stdout, allocator); JsonBencSerializer_get()->serializeDictionary(stdoutWriter, &config); printf("\n"); return 0; } int forceNoBackground = 0; if (argc == 2 && CString_strcmp(argv[1], "--nobg") == 0) { forceNoBackground = 1; } struct Log* logger = FileWriterLog_new(stdout, allocator); // --------------------- Get Admin --------------------- // Dict* configAdmin = Dict_getDict(&config, String_CONST("admin")); String* adminPass = Dict_getString(configAdmin, String_CONST("password")); String* adminBind = Dict_getString(configAdmin, String_CONST("bind")); if (!adminPass) { adminPass = String_newBinary(NULL, 32, allocator); Random_base32(rand, (uint8_t*) adminPass->bytes, 32); adminPass->len = CString_strlen(adminPass->bytes); } if (!adminBind) { Except_throw(eh, "You must specify admin.bind in the cjdroute.conf file."); } // --------------------- Welcome to cjdns ---------------------- // char* sysInfo = SysInfo_describe(SysInfo_detect(), allocator); Log_info(logger, "Cjdns %s %s", ArchInfo_getArchStr(), sysInfo); // --------------------- Check for running instance --------------------- // Log_info(logger, "Checking for running instance..."); checkRunningInstance(allocator, eventBase, adminBind, adminPass, logger, eh); // --------------------- Setup Pipes to Angel --------------------- // struct Allocator* corePipeAlloc = Allocator_child(allocator); char corePipeName[64] = "client-core-"; Random_base32(rand, (uint8_t*)corePipeName+CString_strlen(corePipeName), 31); Assert_ifParanoid(EventBase_eventCount(eventBase) == 0); struct Pipe* corePipe = Pipe_named(corePipeName, eventBase, eh, corePipeAlloc); Assert_ifParanoid(EventBase_eventCount(eventBase) == 2); corePipe->logger = logger; char* args[] = { "core", corePipeName, NULL }; // --------------------- Spawn Angel --------------------- // String* privateKey = Dict_getString(&config, String_CONST("privateKey")); char* corePath = Process_getPath(allocator); if (!corePath) { Except_throw(eh, "Can't find a usable cjdns core executable, " "make sure it is in the same directory as cjdroute"); } if (!privateKey) { Except_throw(eh, "Need to specify privateKey."); } Process_spawn(corePath, args, eventBase, allocator, onCoreExit); // --------------------- Pre-Configure Core ------------------------- // Dict* preConf = Dict_new(allocator); Dict* adminPreConf = Dict_new(allocator); Dict_putDict(preConf, String_CONST("admin"), adminPreConf, allocator); Dict_putString(preConf, String_CONST("privateKey"), privateKey, allocator); Dict_putString(adminPreConf, String_CONST("bind"), adminBind, allocator); Dict_putString(adminPreConf, String_CONST("pass"), adminPass, allocator); Dict* logging = Dict_getDict(&config, String_CONST("logging")); if (logging) { Dict_putDict(preConf, String_CONST("logging"), logging, allocator); } struct Message* toCoreMsg = Message_new(0, 1024, allocator); BencMessageWriter_write(preConf, toCoreMsg, eh); Iface_CALL(corePipe->iface.send, toCoreMsg, &corePipe->iface); Log_debug(logger, "Sent [%d] bytes to core", toCoreMsg->length); // --------------------- Get Response from Core --------------------- // struct Message* fromCoreMsg = InterfaceWaiter_waitForData(&corePipe->iface, eventBase, allocator, eh); Dict* responseFromCore = BencMessageReader_read(fromCoreMsg, allocator, eh); // --------------------- Close the Core Pipe --------------------- // Allocator_free(corePipeAlloc); corePipe = NULL; // --------------------- Get Admin Addr/Port/Passwd --------------------- // Dict* responseFromCoreAdmin = Dict_getDict(responseFromCore, String_CONST("admin")); adminBind = Dict_getString(responseFromCoreAdmin, String_CONST("bind")); if (!adminBind) { Except_throw(eh, "didn't get address and port back from core"); } struct Sockaddr_storage adminAddr; if (Sockaddr_parse(adminBind->bytes, &adminAddr)) { Except_throw(eh, "Unable to parse [%s] as an ip address port, eg: 127.0.0.1:11234", adminBind->bytes); } Assert_ifParanoid(EventBase_eventCount(eventBase) == 1); // --------------------- Configuration ------------------------- // Configurator_config(&config, &adminAddr.addr, adminPass, eventBase, logger, allocator); // --------------------- noBackground ------------------------ // int64_t* noBackground = Dict_getInt(&config, String_CONST("noBackground")); if (forceNoBackground || (noBackground && *noBackground)) { Log_debug(logger, "Keeping cjdns client alive because %s", (forceNoBackground) ? "--nobg was specified on the command line" : "noBackground was set in the configuration"); EventBase_beginLoop(eventBase); } // Freeing this allocator here causes the core to be terminated in the epoll syscall. //Allocator_free(allocator); return 0; }