X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Finvitation.c;h=36dfa41b64b1d2a62a04f66d66006e72c4a987ba;hb=82575bd44dc02bd1febd265c1db0f05b298329af;hp=a5454bf449af45b14dd518a138c501fce8ac8c97;hpb=37cca72e6c973b77b5d11dcf721ae050edc23586;p=tinc diff --git a/src/invitation.c b/src/invitation.c index a5454bf4..36dfa41b 100644 --- a/src/invitation.c +++ b/src/invitation.c @@ -27,17 +27,12 @@ #include "names.h" #include "netutl.h" #include "rsagen.h" +#include "script.h" #include "sptps.h" #include "tincctl.h" #include "utils.h" #include "xalloc.h" -#ifdef HAVE_MINGW -#define SCRIPTEXTENSION ".bat" -#else -#define SCRIPTEXTENSION "" -#endif - int addressfamily = AF_UNSPEC; char *get_my_hostname() { @@ -90,12 +85,14 @@ char *get_my_hostname() { // If that doesn't work, guess externally visible hostname fprintf(stderr, "Trying to discover externally visible hostname...\n"); - struct addrinfo *ai = str2addrinfo("ifconfig.me", "80", SOCK_STREAM); - static const char request[] = "GET /host HTTP/1.0\r\n\r\n"; - if(ai) { - int s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + struct addrinfo *ai = str2addrinfo("tinc-vpn.org", "80", SOCK_STREAM); + struct addrinfo *aip = ai; + static const char request[] = "GET http://tinc-vpn.org/host.cgi HTTP/1.0\r\n\r\n"; + + while(aip) { + int s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if(s >= 0) { - if(connect(s, ai->ai_addr, ai->ai_addrlen)) { + if(connect(s, aip->ai_addr, aip->ai_addrlen)) { closesocket(s); s = -1; } @@ -112,14 +109,20 @@ char *get_my_hostname() { hostname = xstrdup(p + 1); } closesocket(s); + if(hostname) + break; } - freeaddrinfo(ai); + aip = aip->ai_next; + continue; } + if(ai) + freeaddrinfo(ai); + // Check that the hostname is reasonable if(hostname) { for(char *p = hostname; *p; p++) { - if(isalnum(*p) || *p == '-' || *p == '.') + if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':') continue; // If not, forget it. free(hostname); @@ -347,37 +350,76 @@ int cmd_invite(int argc, char *argv[]) { // Create a random cookie for this invitation. char cookie[25]; randomize(cookie, 18); + + // Create a filename that doesn't reveal the cookie itself + char buf[18 + strlen(fingerprint)]; + char cookiehash[25]; + memcpy(buf, cookie, 18); + memcpy(buf + 18, fingerprint, sizeof buf - 18); + digest_create(digest, buf, sizeof buf, cookiehash); + b64encode_urlsafe(cookiehash, cookiehash, 18); + b64encode_urlsafe(cookie, cookie, 18); // Create a file containing the details of the invitation. - xasprintf(&filename, "%s" SLASH "invitations" SLASH "%s", confbase, cookie); + xasprintf(&filename, "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash); int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600); if(!ifd) { fprintf(stderr, "Could not create invitation file %s: %s\n", filename, strerror(errno)); free(filename); return 1; } - free(filename); f = fdopen(ifd, "w"); if(!f) abort(); + // Get the local address + char *address = get_my_hostname(); + // Fill in the details. fprintf(f, "Name = %s\n", argv[1]); if(netname) fprintf(f, "NetName = %s\n", netname); fprintf(f, "ConnectTo = %s\n", myname); - // TODO: copy Broadcast and Mode + + // Copy Broadcast and Mode + FILE *tc = fopen(tinc_conf, "r"); + if(tc) { + char buf[1024]; + while(fgets(buf, sizeof buf, tc)) { + if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4])) + || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) + fputs(buf, f); + } + fclose(tc); + } + fprintf(f, "#---------------------------------------------------------------#\n"); fprintf(f, "Name = %s\n", myname); - xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, myname); - fcopy(f, filename); + char *filename2; + xasprintf(&filename2, "%s" SLASH "hosts" SLASH "%s", confbase, myname); + fcopy(f, filename2); fclose(f); + free(filename2); // Create an URL from the local address, key hash and cookie - char *address = get_my_hostname(); - printf("%s/%s%s\n", address, hash, cookie); + char *url; + xasprintf(&url, "%s/%s%s", address, hash, cookie); + + // Call the inviation-created script + char *envp[6] = {}; + xasprintf(&envp[0], "NAME=%s", myname); + xasprintf(&envp[1], "NETNAME=%s", netname); + xasprintf(&envp[2], "NODE=%s", argv[1]); + xasprintf(&envp[3], "INVITATION_FILE=%s", filename); + xasprintf(&envp[4], "INVITATION_URL=%s", url); + execute_script("invitation-created", envp); + for(int i = 0; i < 6 && envp[i]; i++) + free(envp[i]); + + puts(url); + free(url); free(filename); free(address); @@ -490,6 +532,9 @@ static bool finalize_join(void) { if(!netname) netname = grep(data, "NetName"); + bool ask_netname = false; + char temp_netname[32]; + make_names: if(!confbasegiven) { free(confbase); @@ -508,26 +553,20 @@ make_names: fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf); if(!tty || confbasegiven) return false; -ask_netname: - fprintf(stderr, "Enter a new netname: "); - if(!fgets(line, sizeof line, stdin)) { - fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno)); - return false; - } - if(!*line || *line == '\n') - goto ask_netname; - line[strlen(line) - 1] = 0; - netname = line; + // Generate a random netname, ask for a better one later. + ask_netname = true; + snprintf(temp_netname, sizeof temp_netname, "join_%x", rand()); + netname = temp_netname; goto make_names; } - if(mkdir(confbase, 0755) && errno != EEXIST) { + if(mkdir(confbase, 0777) && errno != EEXIST) { fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno)); return false; } - if(mkdir(hosts_dir, 0755) && errno != EEXIST) { + if(mkdir(hosts_dir, 0777) && errno != EEXIST) { fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno)); return false; } @@ -655,12 +694,7 @@ ask_netname: return false; xasprintf(&filename, "%s" SLASH "ecdsa_key.priv", confbase); - f = fopen(filename, "w"); - -#ifdef HAVE_FCHMOD - /* Make it unreadable for others. */ - fchmod(fileno(f), 0600); -#endif + f = fopenmask(filename, "w", 0600); if(!ecdsa_write_pem_private_key(key, f)) { fprintf(stderr, "Error writing private key!\n"); @@ -679,12 +713,7 @@ ask_netname: rsa_t *rsa = rsa_generate(2048, 0x1001); xasprintf(&filename, "%s" SLASH "rsa_key.priv", confbase); - f = fopen(filename, "w"); - -#ifdef HAVE_FCHMOD - /* Make it unreadable for others. */ - fchmod(fileno(f), 0600); -#endif + f = fopenmask(filename, "w", 0600); rsa_write_pem_private_key(rsa, f); fclose(f); @@ -697,13 +726,35 @@ ask_netname: check_port(name); - fprintf(stderr, "Invitation succesfully accepted.\n"); - shutdown(sock, SHUT_RDWR); - success = true; +ask_netname: + if(ask_netname) { + fprintf(stderr, "Enter a new netname: "); + if(!fgets(line, sizeof line, stdin)) { + fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno)); + return false; + } + if(!*line || *line == '\n') + goto ask_netname; + + line[strlen(line) - 1] = 0; + + char *newbase; + xasprintf(&newbase, CONFDIR SLASH "tinc" SLASH "%s", line); + if(rename(confbase, newbase)) { + fprintf(stderr, "Error trying to rename %s to %s: %s\n", confbase, newbase, strerror(errno)); + free(newbase); + goto ask_netname; + } + + free(newbase); + netname = line; + make_names(); + } return true; } + static bool invitation_send(void *handle, uint8_t type, const char *data, size_t len) { while(len) { int result = send(sock, data, len, 0); @@ -732,6 +783,12 @@ static bool invitation_receive(void *handle, uint8_t type, const char *msg, uint case 1: return finalize_join(); + case 2: + fprintf(stderr, "Invitation succesfully accepted.\n"); + shutdown(sock, SHUT_RDWR); + success = true; + break; + default: return false; } @@ -749,9 +806,25 @@ int cmd_join(int argc, char *argv[]) { return 1; } - // Make sure confdir exists. - if(mkdir(confdir, 0755) && errno != EEXIST) { - fprintf(stderr, "Could not create directory %s: %s\n", CONFDIR, strerror(errno)); + // Make sure confbase exists and is accessible. + if(strcmp(confdir, confbase) && mkdir(confdir, 0755) && errno != EEXIST) { + fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno)); + return 1; + } + + if(mkdir(confbase, 0777) && errno != EEXIST) { + fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno)); + return 1; + } + + if(access(confbase, R_OK | W_OK | X_OK)) { + fprintf(stderr, "No permission to write in directory %s: %s\n", confbase, strerror(errno)); + return 1; + } + + // If a netname or explicit configuration directory is specified, check for an existing tinc.conf. + if((netname || confbasegiven) && !access(tinc_conf, F_OK)) { + fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf); return 1; } @@ -853,7 +926,7 @@ int cmd_join(int argc, char *argv[]) { return 1; } - // Check if the hash of the key he have us matches the hash in the URL. + // Check if the hash of the key he gave us matches the hash in the URL. char *fingerprint = line + 2; digest_t *digest = digest_open_by_name("sha256", 18); if(!digest)