Add strict checks to hex to binary conversions.
[oweals/tinc.git] / src / net_setup.c
index 2684514392dac017d4b3faeba8f8b4aa510bce69..a28ab7ad147c7e3989bba3c3231861028098d1c4 100644 (file)
 char *myport;
 devops_t devops;
 
+char *proxyhost;
+char *proxyport;
+char *proxyuser;
+char *proxypass;
+proxytype_t proxytype;
+
 bool read_rsa_public_key(connection_t *c) {
        FILE *fp;
        char *fname;
@@ -60,7 +66,10 @@ bool read_rsa_public_key(connection_t *c) {
        /* First, check for simple PublicKey statement */
 
        if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) {
-               BN_hex2bn(&c->rsa_key->n, key);
+               if(BN_hex2bn(&c->rsa_key->n, key) != strlen(key)) {
+                       logger(LOG_ERR, "Invalid PublicKey for %s!", c->name);
+                       return false;
+               }
                BN_hex2bn(&c->rsa_key->e, "FFFF");
                free(key);
                return true;
@@ -163,8 +172,14 @@ static bool read_rsa_private_key(void) {
                }
                myself->connection->rsa_key = RSA_new();
 //             RSA_blinding_on(myself->connection->rsa_key, NULL);
-               BN_hex2bn(&myself->connection->rsa_key->d, key);
-               BN_hex2bn(&myself->connection->rsa_key->n, pubkey);
+               if(BN_hex2bn(&myself->connection->rsa_key->d, key) != strlen(key)) {
+                       logger(LOG_ERR, "Invalid PrivateKey for myself!");
+                       return false;
+               }
+               if(BN_hex2bn(&myself->connection->rsa_key->n, pubkey) != strlen(pubkey)) {
+                       logger(LOG_ERR, "Invalid PublicKey for myself!");
+                       return false;
+               }
                BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
                free(key);
                free(pubkey);
@@ -269,6 +284,44 @@ void load_all_subnets(void) {
        closedir(dir);
 }
 
+char *get_name(void) {
+       char *name = NULL;
+
+       get_config_string(lookup_config(config_tree, "Name"), &name);
+
+       if(!name)
+               return NULL;
+
+       if(*name == '$') {
+               char *envname = getenv(name + 1);
+               if(!envname) {
+                       if(strcmp(name + 1, "HOST")) {
+                               fprintf(stderr, "Invalid Name: environment variable %s does not exist\n", name + 1);
+                               return false;
+                       }
+                       envname = alloca(32);
+                       if(gethostname(envname, 32)) {
+                               fprintf(stderr, "Could not get hostname: %s\n", strerror(errno));
+                               return false;
+                       }
+                       envname[31] = 0;
+               }
+               free(name);
+               name = xstrdup(envname);
+               for(char *c = name; *c; c++)
+                       if(!isalnum(*c))
+                               *c = '_';
+       }
+
+       if(!check_id(name)) {
+               logger(LOG_ERR, "Invalid name for myself!");
+               free(name);
+               return false;
+       }
+
+       return name;
+}
+
 /*
   Configure node_t myself and set up the local sockets (listen only)
 */
@@ -278,6 +331,8 @@ static bool setup_myself(void) {
        char *name, *hostname, *mode, *afname, *cipher, *digest, *type;
        char *fname = NULL;
        char *address = NULL;
+       char *proxy = NULL;
+       char *space;
        char *envp[5];
        struct addrinfo *ai, *aip, hint = {0};
        bool choice;
@@ -293,17 +348,11 @@ static bool setup_myself(void) {
        myself->connection->options = 0;
        myself->connection->protocol_version = PROT_CURRENT;
 
-       if(!get_config_string(lookup_config(config_tree, "Name"), &name)) {     /* Not acceptable */
+       if(!(name = get_name())) {
                logger(LOG_ERR, "Name for tinc daemon required!");
                return false;
        }
 
-       if(!check_id(name)) {
-               logger(LOG_ERR, "Invalid name for myself!");
-               free(name);
-               return false;
-       }
-
        myself->name = name;
        myself->connection->name = xstrdup(name);
        xasprintf(&fname, "%s/hosts/%s", confbase, name);
@@ -327,6 +376,68 @@ static bool setup_myself(void) {
                sockaddr2str(&sa, NULL, &myport);
        }
 
+       get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
+       if(proxy) {
+               if((space = strchr(proxy, ' ')))
+                       *space++ = 0;
+
+               if(!strcasecmp(proxy, "none")) {
+                       proxytype = PROXY_NONE;
+               } else if(!strcasecmp(proxy, "socks4")) {
+                       proxytype = PROXY_SOCKS4;
+               } else if(!strcasecmp(proxy, "socks4a")) {
+                       proxytype = PROXY_SOCKS4A;
+               } else if(!strcasecmp(proxy, "socks5")) {
+                       proxytype = PROXY_SOCKS5;
+               } else if(!strcasecmp(proxy, "http")) {
+                       proxytype = PROXY_HTTP;
+               } else if(!strcasecmp(proxy, "exec")) {
+                       proxytype = PROXY_EXEC;
+               } else {
+                       logger(LOG_ERR, "Unknown proxy type %s!", proxy);
+                       return false;
+               }
+
+               switch(proxytype) {
+                       case PROXY_NONE:
+                       default:
+                               break;
+
+                       case PROXY_EXEC:
+                               if(!space || !*space) {
+                                       logger(LOG_ERR, "Argument expected for proxy type exec!");
+                                       return false;
+                               }
+                               proxyhost =  xstrdup(space);
+                               break;
+
+                       case PROXY_SOCKS4:
+                       case PROXY_SOCKS4A:
+                       case PROXY_SOCKS5:
+                       case PROXY_HTTP:
+                               proxyhost = space;
+                               if(space && (space = strchr(space, ' ')))
+                                       *space++ = 0, proxyport = space;
+                               if(space && (space = strchr(space, ' ')))
+                                       *space++ = 0, proxyuser = space;
+                               if(space && (space = strchr(space, ' ')))
+                                       *space++ = 0, proxypass = space;
+                               if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
+                                       logger(LOG_ERR, "Host and port argument expected for proxy!");
+                                       return false;
+                               }
+                               proxyhost = xstrdup(proxyhost);
+                               proxyport = xstrdup(proxyport);
+                               if(proxyuser && *proxyuser)
+                                       proxyuser = xstrdup(proxyuser);
+                               if(proxypass && *proxypass)
+                                       proxypass = xstrdup(proxypass);
+                               break;
+               }
+
+               free(proxy);
+       }
+
        /* Read in all the subnets specified in the host configuration file */
 
        cfg = lookup_config(config_tree, "Subnet");
@@ -397,7 +508,19 @@ static bool setup_myself(void) {
 
        get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
        get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl);
-       get_config_bool(lookup_config(config_tree, "Broadcast"), &broadcast);
+       if(get_config_string(lookup_config(config_tree, "Broadcast"), &mode)) {
+               if(!strcasecmp(mode, "no"))
+                       broadcast_mode = BMODE_NONE;
+               else if(!strcasecmp(mode, "yes") || !strcasecmp(mode, "mst"))
+                       broadcast_mode = BMODE_MST;
+               else if(!strcasecmp(mode, "direct"))
+                       broadcast_mode = BMODE_DIRECT;
+               else {
+                       logger(LOG_ERR, "Invalid broadcast mode!");
+                       return false;
+               }
+               free(mode);
+       }
 
 #if !defined(SOL_IP) || !defined(IP_TOS)
        if(priorityinheritance)
@@ -548,6 +671,8 @@ static bool setup_myself(void) {
                        devops = dummy_devops;
                else if(!strcasecmp(type, "raw_socket"))
                        devops = raw_socket_devops;
+               else if(!strcasecmp(type, "multicast"))
+                       devops = multicast_devops;
 #ifdef ENABLE_UML
                else if(!strcasecmp(type, "uml"))
                        devops = uml_devops;
@@ -579,58 +704,112 @@ static bool setup_myself(void) {
 
        /* Open sockets */
 
-       listen_sockets = 0;
-       cfg = lookup_config(config_tree, "BindToAddress");
-
-       do {
-               get_config_string(cfg, &address);
-               if(cfg)
-                       cfg = lookup_config_next(config_tree, cfg);
-
-               hint.ai_family = addressfamily;
-               hint.ai_socktype = SOCK_STREAM;
-               hint.ai_protocol = IPPROTO_TCP;
-               hint.ai_flags = AI_PASSIVE;
+       if(!do_detach && getenv("LISTEN_FDS")) {
+               sockaddr_t sa;
+               socklen_t salen;
 
-               err = getaddrinfo(address, myport, &hint, &ai);
-               free(address);
+               listen_sockets = atoi(getenv("LISTEN_FDS"));
+#ifdef HAVE_UNSETENV
+               unsetenv("LISTEN_FDS");
+#endif
 
-               if(err || !ai) {
-                       logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
-                                  gai_strerror(err));
+               if(listen_sockets > MAXSOCKETS) {
+                       logger(LOG_ERR, "Too many listening sockets");
                        return false;
                }
 
-               for(aip = ai; aip; aip = aip->ai_next) {
-                       if(listen_sockets >= MAXSOCKETS) {
-                               logger(LOG_ERR, "Too many listening sockets");
+               for(i = 0; i < listen_sockets; i++) {
+                       salen = sizeof sa;
+                       if(getsockname(i + 3, &sa.sa, &salen) < 0) {
+                               logger(LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(errno));
                                return false;
                        }
 
-                       listen_socket[listen_sockets].tcp =
-                               setup_listen_socket((sockaddr_t *) aip->ai_addr);
-
-                       if(listen_socket[listen_sockets].tcp < 0)
-                               continue;
+                       listen_socket[i].tcp = i + 3;
 
-                       listen_socket[listen_sockets].udp =
-                               setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
+#ifdef FD_CLOEXEC
+                       fcntl(i + 3, F_SETFD, FD_CLOEXEC);
+#endif
 
-                       if(listen_socket[listen_sockets].udp < 0)
-                               continue;
+                       listen_socket[i].udp = setup_vpn_in_socket(&sa);
+                       if(listen_socket[i].udp < 0)
+                               return false;
 
                        ifdebug(CONNECTIONS) {
-                               hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
+                               hostname = sockaddr2hostname(&sa);
                                logger(LOG_NOTICE, "Listening on %s", hostname);
                                free(hostname);
                        }
 
-                       memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
-                       listen_sockets++;
+                       memcpy(&listen_socket[i].sa, &sa, salen);
                }
+       } else {
+               listen_sockets = 0;
+               cfg = lookup_config(config_tree, "BindToAddress");
+
+               do {
+                       get_config_string(cfg, &address);
+                       if(cfg)
+                               cfg = lookup_config_next(config_tree, cfg);
+
+                       char *port = myport;
+
+                       if(address) {
+                               char *space = strchr(address, ' ');
+                               if(space) {
+                                       *space++ = 0;
+                                       port = space;
+                               }
+
+                               if(!strcmp(address, "*"))
+                                       *address = 0;
+                       }
+
+                       hint.ai_family = addressfamily;
+                       hint.ai_socktype = SOCK_STREAM;
+                       hint.ai_protocol = IPPROTO_TCP;
+                       hint.ai_flags = AI_PASSIVE;
+
+                       err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
+                       free(address);
+
+                       if(err || !ai) {
+                               logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
+                                          gai_strerror(err));
+                               return false;
+                       }
+
+                       for(aip = ai; aip; aip = aip->ai_next) {
+                               if(listen_sockets >= MAXSOCKETS) {
+                                       logger(LOG_ERR, "Too many listening sockets");
+                                       return false;
+                               }
 
-               freeaddrinfo(ai);
-       } while(cfg);
+                               listen_socket[listen_sockets].tcp =
+                                       setup_listen_socket((sockaddr_t *) aip->ai_addr);
+
+                               if(listen_socket[listen_sockets].tcp < 0)
+                                       continue;
+
+                               listen_socket[listen_sockets].udp =
+                                       setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
+
+                               if(listen_socket[listen_sockets].udp < 0)
+                                       continue;
+
+                               ifdebug(CONNECTIONS) {
+                                       hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
+                                       logger(LOG_NOTICE, "Listening on %s", hostname);
+                                       free(hostname);
+                               }
+
+                               memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
+                               listen_sockets++;
+                       }
+
+                       freeaddrinfo(ai);
+               } while(cfg);
+       }
 
        if(listen_sockets)
                logger(LOG_NOTICE, "Ready");