X-Git-Url: https://www.tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Finvitation.c;h=22fd6146030b29cbb7d4e90f9bcec24da47535e8;hb=373b0c12d9d0e8a3b449fd18be704e28dd6403e1;hp=5dc8c6300e452819caa3c963b44b843886bcf9a8;hpb=356118324f7cde276f393162fca54040f8c67f04;p=tinc diff --git a/src/invitation.c b/src/invitation.c index 5dc8c630..22fd6146 100644 --- a/src/invitation.c +++ b/src/invitation.c @@ -1,6 +1,6 @@ /* invitation.c -- Create and accept invitations - Copyright (C) 2013-2017 Guus Sliepen + Copyright (C) 2013-2022 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -97,7 +97,7 @@ static void scan_for_hostname(const char *filename, char **hostname, char **port fclose(f); } -char *get_my_hostname() { +static char *get_my_hostname(void) { char *hostname = NULL; char *port = NULL; char *hostport = NULL; @@ -111,6 +111,9 @@ char *get_my_hostname() { scan_for_hostname(tinc_conf, &hostname, &port); } + free(name); + name = NULL; + if(hostname) { goto done; } @@ -133,7 +136,7 @@ char *get_my_hostname() { if(s >= 0) { send(s, request, sizeof(request) - 1, 0); - int len = recv(s, line, sizeof(line) - 1, MSG_WAITALL); + ssize_t len = recv(s, line, sizeof(line) - 1, MSG_WAITALL); if(len > 0) { line[len] = 0; @@ -167,7 +170,7 @@ char *get_my_hostname() { // Check that the hostname is reasonable if(hostname) { for(char *p = hostname; *p; p++) { - if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':') { + if(isalnum((uint8_t) *p) || *p == '-' || *p == '.' || *p == ':') { continue; } @@ -181,6 +184,7 @@ char *get_my_hostname() { if(!tty) { if(!hostname) { fprintf(stderr, "Could not determine the external address or hostname. Please set Address manually.\n"); + free(port); return NULL; } @@ -199,6 +203,7 @@ again: if(!fgets(line, sizeof(line), stdin)) { fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno)); free(hostname); + free(port); return NULL; } @@ -211,7 +216,7 @@ again: } for(char *p = line; *p; p++) { - if(isalnum(*p) || *p == '-' || *p == '.') { + if(isalnum((uint8_t) *p) || *p == '-' || *p == '.') { continue; } @@ -287,6 +292,7 @@ int cmd_invite(int argc, char *argv[]) { return 1; } + free(myname); myname = get_my_name(true); if(!myname) { @@ -353,7 +359,11 @@ int cmd_invite(int argc, char *argv[]) { char invname[PATH_MAX]; struct stat st; - snprintf(invname, sizeof(invname), "%s" SLASH "%s", filename, ent->d_name); + + if((size_t)snprintf(invname, sizeof(invname), "%s" SLASH "%s", filename, ent->d_name) >= sizeof(invname)) { + fprintf(stderr, "Filename too long: %s" SLASH "%s\n", filename, ent->d_name); + continue; + } if(!stat(invname, &st)) { if(deadline < st.st_mtime) { @@ -401,6 +411,7 @@ int cmd_invite(int argc, char *argv[]) { if(!f) { fprintf(stderr, "Could not write %s: %s\n", filename, strerror(errno)); + ecdsa_free(key); return 1; } @@ -409,13 +420,16 @@ int cmd_invite(int argc, char *argv[]) { if(!ecdsa_write_pem_private_key(key, f)) { fprintf(stderr, "Could not write ECDSA private key\n"); fclose(f); + ecdsa_free(key); return 1; } fclose(f); - if(connect_tincd(false)) { + if(connect_tincd(true)) { sendline(fd, "%d %d", CONTROL, REQ_RELOAD); + } else { + fprintf(stderr, "Could not signal the tinc daemon. Please restart or reload it manually.\n"); } } else { key = ecdsa_read_pem_private_key(f); @@ -434,21 +448,27 @@ int cmd_invite(int argc, char *argv[]) { char hash[64]; char *fingerprint = ecdsa_get_base64_public_key(key); sha512(fingerprint, strlen(fingerprint), hash); - b64encode_urlsafe(hash, hash, 18); + b64encode_tinc_urlsafe(hash, hash, 18); + + ecdsa_free(key); // 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)]; + const size_t buflen = 18 + strlen(fingerprint); + uint8_t *buf = alloca(buflen); + char cookiehash[64]; memcpy(buf, cookie, 18); - memcpy(buf + 18, fingerprint, sizeof(buf) - 18); - sha512(buf, sizeof(buf), cookiehash); - b64encode_urlsafe(cookiehash, cookiehash, 18); + memcpy(buf + 18, fingerprint, buflen - 18); + sha512(buf, buflen, cookiehash); + b64encode_tinc_urlsafe(cookiehash, cookiehash, 18); + + free(fingerprint); - b64encode_urlsafe(cookie, cookie, 18); + b64encode_tinc_urlsafe(cookie, cookie, 18); // Create a file containing the details of the invitation. snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash); @@ -527,15 +547,13 @@ int cmd_invite(int argc, char *argv[]) { } static int sock; -static char cookie[18]; +static char cookie[18], hash[18]; static sptps_t sptps; static char *data; static size_t datalen; static bool success = false; -static char cookie[18], hash[18]; - -static char *get_line(const char **data) { +static char *get_line(char *line, size_t linelen, const char **data) { if(!data || !*data) { return NULL; } @@ -545,16 +563,15 @@ static char *get_line(const char **data) { return NULL; } - static char line[1024]; const char *end = strchr(*data, '\n'); - size_t len = end ? end - *data : strlen(*data); + size_t len = end ? (size_t)(end - *data) : strlen(*data); - if(len >= sizeof(line)) { + if(len >= linelen) { fprintf(stderr, "Maximum line length exceeded!\n"); return NULL; } - if(len && !isprint(**data)) { + if(len && !isprint((uint8_t) **data)) { abort(); } @@ -571,7 +588,9 @@ static char *get_line(const char **data) { } static char *get_value(const char *data, const char *var) { - char *line = get_line(&data); + static char buf[1024]; + + char *line = get_line(buf, sizeof(buf), &data); if(!line) { return NULL; @@ -597,7 +616,7 @@ static char *grep(const char *data, const char *var) { static char value[1024]; const char *p = data; - int varlen = strlen(var); + size_t varlen = strlen(var); // Skip all lines not starting with var while(strncasecmp(p, var, varlen) || !strchr(" \t=", p[varlen])) { @@ -627,7 +646,7 @@ static char *grep(const char *data, const char *var) { return xstrdup(p); } - if(e - p >= sizeof(value)) { + if((size_t)(e - p) >= sizeof(value)) { fprintf(stderr, "Maximum line length exceeded!\n"); return NULL; } @@ -638,7 +657,7 @@ static char *grep(const char *data, const char *var) { } static bool finalize_join(void) { - char *name = xstrdup(get_value(data, "Name")); + const char *name = get_value(data, "Name"); if(!name) { fprintf(stderr, "No Name found in invitation!\n"); @@ -651,7 +670,7 @@ static bool finalize_join(void) { } if(!netname) { - netname = grep(data, "NetName"); + netname = xstrdup(grep(data, "NetName")); if(netname && !check_netname(netname, true)) { fprintf(stderr, "Unsafe NetName found in invitation!\n"); @@ -686,7 +705,7 @@ make_names: // Generate a random netname, ask for a better one later. ask_netname = true; - snprintf(temp_netname, sizeof(temp_netname), "join_%x", rand()); + snprintf(temp_netname, sizeof(temp_netname), "join_%x", prng(UINT32_MAX)); netname = temp_netname; goto make_names; } @@ -751,14 +770,16 @@ make_names: const char *p = data; char *l, *value; - while((l = get_line(&p))) { + static char line[1024]; + + while((l = get_line(line, sizeof(line), &p))) { // Ignore comments if(*l == '#') { continue; } // Split line into variable and value - int len = strcspn(l, "\t ="); + size_t len = strcspn(l, "\t ="); value = l + len; value += strspn(value, "\t "); @@ -823,8 +844,12 @@ make_names: fprintf(stderr, "Ignoring unknown variable '%s' in invitation.\n", l); continue; } else if(!(variables[i].type & VAR_SAFE)) { - fprintf(stderr, "Ignoring unsafe variable '%s' in invitation.\n", l); - continue; + if(force) { + fprintf(stderr, "Warning: unsafe variable '%s' in invitation.\n", l); + } else { + fprintf(stderr, "Ignoring unsafe variable '%s' in invitation.\n", l); + continue; + } } // Copy the safe variable to the right config file @@ -854,12 +879,12 @@ make_names: return false; } - while((l = get_line(&p))) { + while((l = get_line(line, sizeof(line), &p))) { if(!strcmp(l, "#---------------------------------------------------------------#")) { continue; } - int len = strcspn(l, "\t ="); + size_t len = strcspn(l, "\t ="); if(len == 4 && !strncasecmp(l, "Name", 4)) { value = l + len; @@ -953,7 +978,11 @@ ask_netname: line[strlen(line) - 1] = 0; char newbase[PATH_MAX]; - snprintf(newbase, sizeof(newbase), CONFDIR SLASH "tinc" SLASH "%s", line); + + if((size_t)snprintf(newbase, sizeof(newbase), CONFDIR SLASH "tinc" SLASH "%s", line) >= sizeof(newbase)) { + fprintf(stderr, "Filename too long: " CONFDIR SLASH "tinc" SLASH "%s\n", line); + goto ask_netname; + } if(rename(confbase, newbase)) { fprintf(stderr, "Error trying to rename %s to %s: %s\n", confbase, newbase, strerror(errno)); @@ -966,7 +995,12 @@ ask_netname: char filename2[PATH_MAX]; snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up.invitation", confbase); + +#ifdef HAVE_WINDOWS + snprintf(filename2, sizeof(filename2), "%s" SLASH "tinc-up.bat", confbase); +#else snprintf(filename2, sizeof(filename2), "%s" SLASH "tinc-up", confbase); +#endif if(valid_tinc_up) { if(tty) { @@ -994,8 +1028,18 @@ ask_netname: if(response == 'e') { char *command; -#ifndef HAVE_MINGW - xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ? : getenv("EDITOR") ? : "vi", filename); +#ifndef HAVE_WINDOWS + const char *editor = getenv("VISUAL"); + + if(!editor) { + editor = getenv("EDITOR"); + } + + if(!editor) { + editor = "vi"; + } + + xasprintf(&command, "\"%s\" \"%s\"", editor, filename); #else xasprintf(&command, "edit \"%s\"", filename); #endif @@ -1018,7 +1062,13 @@ ask_netname: } } } else { - fprintf(stderr, "A tinc-up script was generated, but has been left disabled.\n"); + if(force) { + rename(filename, filename2); + chmod(filename2, 0755); + fprintf(stderr, "tinc-up enabled.\n"); + } else { + fprintf(stderr, "A tinc-up script was generated, but has been left disabled.\n"); + } } } else { // A placeholder was generated. @@ -1032,11 +1082,15 @@ ask_netname: } -static bool invitation_send(void *handle, uint8_t type, const void *data, size_t len) { +static bool invitation_send(void *handle, uint8_t type, const void *vdata, size_t len) { + (void)handle; + (void)type; + const char *data = vdata; + while(len) { - int result = send(sock, data, len, 0); + ssize_t result = send(sock, data, len, 0); - if(result == -1 && errno == EINTR) { + if(result == -1 && sockwouldblock(sockerrno)) { continue; } else if(result <= 0) { return false; @@ -1050,6 +1104,8 @@ static bool invitation_send(void *handle, uint8_t type, const void *data, size_t } static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint16_t len) { + (void)handle; + switch(type) { case SPTPS_HANDSHAKE: return sptps_send_record(&sptps, 0, cookie, sizeof(cookie)); @@ -1065,7 +1121,7 @@ static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint return finalize_join(); case 2: - fprintf(stderr, "Invitation succesfully accepted.\n"); + fprintf(stderr, "Invitation successfully accepted.\n"); shutdown(sock, SHUT_RDWR); success = true; break; @@ -1169,10 +1225,11 @@ int cmd_join(int argc, char *argv[]) { } if(!port || !*port) { - port = "655"; + static char default_port[] = "655"; + port = default_port; } - if(!b64decode(slash, hash, 24) || !b64decode(slash + 24, cookie, 24)) { + if(!b64decode_tinc(slash, hash, 24) || !b64decode_tinc(slash + 24, cookie, 24)) { goto invalid; } @@ -1189,6 +1246,8 @@ int cmd_join(int argc, char *argv[]) { struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM); if(!ai) { + free(b64key); + ecdsa_free(key); return 1; } @@ -1201,6 +1260,9 @@ next: aip = aip->ai_next; if(!aip) { + freeaddrinfo(ai); + free(b64key); + ecdsa_free(key); return 1; } } @@ -1225,9 +1287,9 @@ next: fprintf(stderr, "Connected to %s port %s...\n", address, port); // Tell him we have an invitation, and give him our throw-away key. - int len = snprintf(line, sizeof(line), "0 ?%s %d.%d\n", b64key, PROT_MAJOR, PROT_MINOR); + ssize_t len = snprintf(line, sizeof(line), "0 ?%s %d.%d\n", b64key, PROT_MAJOR, PROT_MINOR); - if(len <= 0 || len >= sizeof(line)) { + if(len <= 0 || (size_t)len >= sizeof(line)) { abort(); } @@ -1246,17 +1308,26 @@ next: goto next; } + freeaddrinfo(ai); + ai = NULL; + aip = NULL; + + free(b64key); + b64key = NULL; + // Check if the hash of the key he gave us matches the hash in the URL. char *fingerprint = line + 2; char hishash[64]; if(sha512(fingerprint, strlen(fingerprint), hishash)) { fprintf(stderr, "Could not create digest\n%s\n", line + 2); + ecdsa_free(key); return 1; } if(memcmp(hishash, hash, 18)) { fprintf(stderr, "Peer has an invalid key!\n%s\n", line + 2); + ecdsa_free(key); return 1; } @@ -1264,50 +1335,67 @@ next: ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint); if(!hiskey) { + ecdsa_free(key); return 1; } // Start an SPTPS session if(!sptps_start(&sptps, NULL, true, false, key, hiskey, "tinc invitation", 15, invitation_send, invitation_receive)) { + ecdsa_free(hiskey); + ecdsa_free(key); return 1; } // Feed rest of input buffer to SPTPS if(!sptps_receive_data(&sptps, buffer, blen)) { - return 1; + success = false; + goto exit; } while((len = recv(sock, line, sizeof(line), 0))) { if(len < 0) { - if(errno == EINTR) { + if(sockwouldblock(sockerrno)) { continue; } - fprintf(stderr, "Error reading data from %s port %s: %s\n", address, port, strerror(errno)); - return 1; +#if HAVE_WINDOWS + + // If socket has been shut down, recv() on Windows returns -1 and sets sockerrno + // to WSAESHUTDOWN, while on UNIX-like operating systems recv() returns 0, so we + // have to do an explicit check here. + if(sockshutdown(sockerrno)) { + break; + } + +#endif + fprintf(stderr, "Error reading data from %s port %s: %s\n", address, port, sockstrerror(sockerrno)); + success = false; + goto exit; } char *p = line; while(len) { - int done = sptps_receive_data(&sptps, p, len); + size_t done = sptps_receive_data(&sptps, p, len); if(!done) { - return 1; + success = false; + goto exit; } - len -= done; + len -= (ssize_t) done; p += done; } } +exit: sptps_stop(&sptps); ecdsa_free(hiskey); ecdsa_free(key); closesocket(sock); if(!success) { - fprintf(stderr, "Connection closed by peer, invitation cancelled.\n"); + fprintf(stderr, "Invitation cancelled.\n"); return 1; }