X-Git-Url: https://www.tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Finvitation.c;h=2990c4746c15ea33717de9a440e002e755bff793;hb=3a316823b971396a428f020f401b9fe41252d98d;hp=415c23772dcb09f6238839f92d2640fe352fd05c;hpb=9e3adef5cb31cb73fbbbd25d3fce115aac107714;p=tinc diff --git a/src/invitation.c b/src/invitation.c index 415c2377..2990c474 100644 --- a/src/invitation.c +++ b/src/invitation.c @@ -1,6 +1,6 @@ /* invitation.c -- Create and accept invitations - Copyright (C) 2013-2015 Guus Sliepen + Copyright (C) 2013-2017 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 @@ -23,12 +23,14 @@ #include "crypto.h" #include "ecdsa.h" #include "ecdsagen.h" +#include "ifconfig.h" #include "invitation.h" #include "names.h" #include "netutl.h" #include "rsagen.h" #include "script.h" #include "sptps.h" +#include "subnet.h" #include "tincctl.h" #include "utils.h" #include "xalloc.h" @@ -45,7 +47,7 @@ static void scan_for_hostname(const char *filename, char **hostname, char **port if(!f) return; - while(fgets(line, sizeof line, f)) { + while(fgets(line, sizeof(line), f)) { if(!rstrip(line)) continue; char *p = line, *q; @@ -88,7 +90,7 @@ char *get_my_hostname() { // Use first Address statement in own host config file if(check_id(name)) { - snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name); + snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name); scan_for_hostname(filename, &hostname, &port); scan_for_hostname(tinc_conf, &hostname, &port); } @@ -111,8 +113,8 @@ char *get_my_hostname() { } } if(s >= 0) { - send(s, request, sizeof request - 1, 0); - int len = recv(s, line, sizeof line - 1, MSG_WAITALL); + send(s, request, sizeof(request) - 1, 0); + int len = recv(s, line, sizeof(line) - 1, MSG_WAITALL); if(len > 0) { line[len] = 0; if(line[len - 1] == '\n') @@ -158,7 +160,7 @@ again: fprintf(stderr, " [%s]", hostname); fprintf(stderr, ": "); - if(!fgets(line, sizeof line, stdin)) { + if(!fgets(line, sizeof(line), stdin)) { fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno)); free(hostname); return NULL; @@ -219,7 +221,7 @@ static bool fcopy(FILE *out, const char *filename) { char buf[1024]; size_t len; - while((len = fread(buf, 1, sizeof buf, in))) + while((len = fread(buf, 1, sizeof(buf), in))) fwrite(buf, len, 1, out); fclose(in); return true; @@ -237,27 +239,27 @@ int cmd_invite(int argc, char *argv[]) { return 1; } - char *myname = get_my_name(true); + myname = get_my_name(true); if(!myname) return 1; // Ensure no host configuration file with that name exists char filename[PATH_MAX]; - snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, argv[1]); + snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, argv[1]); if(!access(filename, F_OK)) { fprintf(stderr, "A host config file for %s already exists!\n", argv[1]); return 1; } // If a daemon is running, ensure no other nodes know about this name - bool found = false; if(connect_tincd(false)) { + bool found = false; sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES); - while(recvline(fd, line, sizeof line)) { + while(recvline(fd, line, sizeof(line))) { char node[4096]; int code, req; - if(sscanf(line, "%d %d %s", &code, &req, node) != 3) + if(sscanf(line, "%d %d %4095s", &code, &req, node) != 3) break; if(!strcmp(node, argv[1])) found = true; @@ -269,7 +271,7 @@ int cmd_invite(int argc, char *argv[]) { } } - snprintf(filename, sizeof filename, "%s" SLASH "invitations", confbase); + snprintf(filename, sizeof(filename), "%s" SLASH "invitations", confbase); if(mkdir(filename, 0700) && errno != EEXIST) { fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno)); return 1; @@ -292,7 +294,7 @@ int cmd_invite(int argc, char *argv[]) { continue; char invname[PATH_MAX]; struct stat st; - snprintf(invname, sizeof invname, "%s" SLASH "%s", filename, ent->d_name); + snprintf(invname, sizeof(invname), "%s" SLASH "%s", filename, ent->d_name); if(!stat(invname, &st)) { if(deadline < st.st_mtime) count++; @@ -312,7 +314,7 @@ int cmd_invite(int argc, char *argv[]) { } ecdsa_t *key; - snprintf(filename, sizeof filename, "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase); + snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase); // Remove the key if there are no outstanding invitations. if(!count) @@ -335,7 +337,11 @@ int cmd_invite(int argc, char *argv[]) { return 1; } chmod(filename, 0600); - ecdsa_write_pem_private_key(key, f); + if(!ecdsa_write_pem_private_key(key, f)) { + fprintf(stderr, "Could not write ECDSA private key\n"); + fclose(f); + return 1; + } fclose(f); if(connect_tincd(false)) @@ -364,14 +370,14 @@ int cmd_invite(int argc, char *argv[]) { char buf[18 + strlen(fingerprint)]; char cookiehash[64]; memcpy(buf, cookie, 18); - memcpy(buf + 18, fingerprint, sizeof buf - 18); - sha512(buf, sizeof buf, cookiehash); + memcpy(buf + 18, fingerprint, sizeof(buf) - 18); + sha512(buf, sizeof(buf), cookiehash); b64encode_urlsafe(cookiehash, cookiehash, 18); b64encode_urlsafe(cookie, cookie, 18); // Create a file containing the details of the invitation. - snprintf(filename, sizeof filename, "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash); + snprintf(filename, sizeof(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)); @@ -386,7 +392,7 @@ int cmd_invite(int argc, char *argv[]) { // Fill in the details. fprintf(f, "Name = %s\n", argv[1]); - if(netname) + if(check_netname(netname, true)) fprintf(f, "NetName = %s\n", netname); fprintf(f, "ConnectTo = %s\n", myname); @@ -394,7 +400,7 @@ int cmd_invite(int argc, char *argv[]) { FILE *tc = fopen(tinc_conf, "r"); if(tc) { char buf[1024]; - while(fgets(buf, sizeof buf, tc)) { + 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); @@ -410,7 +416,7 @@ int cmd_invite(int argc, char *argv[]) { fprintf(f, "Name = %s\n", myname); char filename2[PATH_MAX]; - snprintf(filename2, sizeof filename2, "%s" SLASH "hosts" SLASH "%s", confbase, myname); + snprintf(filename2, sizeof(filename2), "%s" SLASH "hosts" SLASH "%s", confbase, myname); fcopy(f, filename2); fclose(f); @@ -419,15 +425,13 @@ int cmd_invite(int argc, char *argv[]) { 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]); + environment_t env; + environment_init(&env); + environment_add(&env, "NODE=%s", argv[1]); + environment_add(&env, "INVITATION_FILE=%s", filename); + environment_add(&env, "INVITATION_URL=%s", url); + execute_script("invitation-created", &env); + environment_exit(&env); puts(url); free(url); @@ -457,7 +461,7 @@ static char *get_line(const char **data) { static char line[1024]; const char *end = strchr(*data, '\n'); size_t len = end ? end - *data : strlen(*data); - if(len >= sizeof line) { + if(len >= sizeof(line)) { fprintf(stderr, "Maximum line length exceeded!\n"); return NULL; } @@ -517,7 +521,7 @@ static char *grep(const char *data, const char *var) { if(!e) return xstrdup(p); - if(e - p >= sizeof value) { + if(e - p >= sizeof(value)) { fprintf(stderr, "Maximum line length exceeded!\n"); return NULL; } @@ -535,12 +539,17 @@ static bool finalize_join(void) { } if(!check_id(name)) { - fprintf(stderr, "Invalid Name found in invitation: %s!\n", name); + fprintf(stderr, "Invalid Name found in invitation!\n"); return false; } - if(!netname) + if(!netname) { netname = grep(data, "NetName"); + if(netname && !check_netname(netname, true)) { + fprintf(stderr, "Unsafe NetName found in invitation!\n"); + return false; + } + } bool ask_netname = false; char temp_netname[32]; @@ -566,7 +575,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", rand()); netname = temp_netname; goto make_names; } @@ -590,7 +599,7 @@ make_names: fprintf(f, "Name = %s\n", name); char filename[PATH_MAX]; - snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name); + snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name); FILE *fh = fopen(filename, "w"); if(!fh) { fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno)); @@ -598,7 +607,30 @@ make_names: return false; } + snprintf(filename, sizeof(filename), "%s" SLASH "invitation-data", confbase); + FILE *finv = fopen(filename, "w"); + if(!finv || fwrite(data, datalen, 1, finv) != 1) { + fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno)); + fclose(fh); + fclose(f); + fclose(finv); + return false; + } + fclose(finv); + + snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up.invitation", confbase); + FILE *fup = fopen(filename, "w"); + if(!fup) { + fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno)); + fclose(f); + fclose(fh); + return false; + } + + ifconfig_header(fup); + // Filter first chunk on approved keywords, split between tinc.conf and hosts/Name + // Generate a tinc-up script from Ifconfig and Route keywords. // Other chunks go unfiltered to their respective host config files const char *p = data; char *l, *value; @@ -637,6 +669,24 @@ make_names: break; } + // Handle Ifconfig and Route statements + if(!found) { + if(!strcasecmp(l, "Ifconfig")) { + if(!strcasecmp(value, "dhcp")) + ifconfig_dhcp(fup); + else if(!strcasecmp(value, "dhcp6")) + ifconfig_dhcp6(fup); + else if(!strcasecmp(value, "slaac")) + ifconfig_slaac(fup); + else + ifconfig_address(fup, value); + continue; + } else if(!strcasecmp(l, "Route")) { + ifconfig_route(fup, value); + continue; + } + } + // Ignore unknown and unsafe variables if(!found) { fprintf(stderr, "Ignoring unknown variable '%s' in invitation.\n", l); @@ -647,10 +697,12 @@ make_names: } // Copy the safe variable to the right config file - fprintf(variables[i].type & VAR_HOST ? fh : f, "%s = %s\n", l, value); + fprintf((variables[i].type & VAR_HOST) ? fh : f, "%s = %s\n", l, value); } fclose(f); + bool valid_tinc_up = ifconfig_footer(fup); + fclose(fup); while(l && !strcasecmp(l, "Name")) { if(!check_id(value)) { @@ -663,7 +715,7 @@ make_names: return false; } - snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, value); + snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, value); f = fopen(filename, "w"); if(!f) { @@ -702,8 +754,10 @@ make_names: if(!b64key) return false; - snprintf(filename, sizeof filename, "%s" SLASH "ed25519_key.priv", confbase); + snprintf(filename, sizeof(filename), "%s" SLASH "ed25519_key.priv", confbase); f = fopenmask(filename, "w", 0600); + if(!f) + return false; if(!ecdsa_write_pem_private_key(key, f)) { fprintf(stderr, "Error writing private key!\n"); @@ -722,13 +776,17 @@ make_names: #ifndef DISABLE_LEGACY rsa_t *rsa = rsa_generate(2048, 0x1001); - snprintf(filename, sizeof filename, "%s" SLASH "rsa_key.priv", confbase); + snprintf(filename, sizeof(filename), "%s" SLASH "rsa_key.priv", confbase); f = fopenmask(filename, "w", 0600); - rsa_write_pem_private_key(rsa, f); + if(!f || !rsa_write_pem_private_key(rsa, f)) { + fprintf(stderr, "Could not write private RSA key\n"); + } else if(!rsa_write_pem_public_key(rsa, fh)) { + fprintf(stderr, "Could not write public RSA key\n"); + } + fclose(f); - rsa_write_pem_public_key(rsa, fh); fclose(fh); rsa_free(rsa); @@ -739,7 +797,7 @@ make_names: ask_netname: if(ask_netname && tty) { fprintf(stderr, "Enter a new netname: "); - if(!fgets(line, sizeof line, stdin)) { + if(!fgets(line, sizeof(line), stdin)) { fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno)); return false; } @@ -749,7 +807,7 @@ ask_netname: line[strlen(line) - 1] = 0; char newbase[PATH_MAX]; - snprintf(newbase, sizeof newbase, CONFDIR SLASH "tinc" SLASH "%s", line); + snprintf(newbase, sizeof(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)); goto ask_netname; @@ -759,6 +817,60 @@ ask_netname: make_names(false); } + char filename2[PATH_MAX]; + snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up.invitation", confbase); + snprintf(filename2, sizeof(filename2), "%s" SLASH "tinc-up", confbase); + + if(valid_tinc_up) { + if(tty) { + FILE *fup = fopen(filename, "r"); + if(fup) { + fprintf(stderr, "\nPlease review the following tinc-up script:\n\n"); + + char buf[MAXSIZE]; + while(fgets(buf, sizeof(buf), fup)) + fputs(buf, stderr); + fclose(fup); + + int response = 0; + do { + fprintf(stderr, "\nDo you want to use this script [y]es/[n]o/[e]dit? "); + response = tolower(getchar()); + } while(!strchr("yne", response)); + + fprintf(stderr, "\n"); + + if(response == 'e') { + char *command; +#ifndef HAVE_MINGW + xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename); +#else + xasprintf(&command, "edit \"%s\"", filename); +#endif + if(system(command)) + response = 'n'; + else + response = 'y'; + free(command); + } + + if(response == 'y') { + rename(filename, filename2); + chmod(filename2, 0755); + fprintf(stderr, "tinc-up enabled.\n"); + } else { + fprintf(stderr, "tinc-up has been left disabled.\n"); + } + } + } else { + fprintf(stderr, "A tinc-up script was generated, but has been left disabled.\n"); + } + } else { + // A placeholder was generated. + rename(filename, filename2); + chmod(filename2, 0755); + } + fprintf(stderr, "Configuration stored in: %s\n", confbase); return true; @@ -781,7 +893,7 @@ 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) { switch(type) { case SPTPS_HANDSHAKE: - return sptps_send_record(&sptps, 0, cookie, sizeof cookie); + return sptps_send_record(&sptps, 0, cookie, sizeof(cookie)); case 0: data = xrealloc(data, datalen + len + 1); @@ -847,7 +959,7 @@ int cmd_join(int argc, char *argv[]) { if(tty) fprintf(stderr, "Enter invitation URL: "); errno = EPIPE; - if(!fgets(line, sizeof line, stdin)) { + if(!fgets(line, sizeof(line), stdin)) { fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno)); return false; } @@ -930,8 +1042,8 @@ 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); - if(len <= 0 || len >= sizeof line) + int len = snprintf(line, sizeof(line), "0 ?%s %d.%d\n", b64key, PROT_MAJOR, PROT_MINOR); + if(len <= 0 || len >= sizeof(line)) abort(); if(!sendline(sock, "0 ?%s %d.%d", b64key, PROT_MAJOR, 1)) { @@ -943,7 +1055,7 @@ next: char hisname[4096] = ""; int code, hismajor, hisminor = 0; - if(!recvline(sock, line, sizeof line) || sscanf(line, "%d %s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(sock, line, sizeof line) || !rstrip(line) || sscanf(line, "%d ", &code) != 1 || code != ACK || strlen(line) < 3) { + if(!recvline(sock, line, sizeof(line)) || sscanf(line, "%d %4095s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(sock, line, sizeof line) || !rstrip(line) || sscanf(line, "%d ", &code) != 1 || code != ACK || strlen(line) < 3) { fprintf(stderr, "Cannot read greeting from peer\n"); closesocket(sock); goto next; @@ -974,7 +1086,7 @@ next: if(!sptps_receive_data(&sptps, buffer, blen)) return 1; - while((len = recv(sock, line, sizeof line, 0))) { + while((len = recv(sock, line, sizeof(line), 0))) { if(len < 0) { if(errno == EINTR) continue;