/*
invitation.c -- Create and accept invitations
- Copyright (C) 2013-2015 Guus Sliepen <guus@tinc-vpn.org>
+ Copyright (C) 2013-2017 Guus Sliepen <guus@tinc-vpn.org>
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
#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"
return 1;
}
- char *myname = get_my_name(true);
+ myname = get_my_name(true);
if(!myname)
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)) {
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;
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))
// 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);
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);
}
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];
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(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;
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);
}
// 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)) {
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");
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);
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;
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;