Generate a tinc-up script from an invitation.
This adds the ability for an invitation to provision an invitee with a tinc-up script. This is quite strictly controlled; only address configuration and routes are supported by adding "Ifconfig" and "Route" statements to the invitation file. The "tinc join" command will generate a tinc-up script from those statements, and will ask before enabling the tinc-up script.
This commit is contained in:
parent
b2200f2166
commit
3273e32541
4 changed files with 282 additions and 0 deletions
|
@ -104,6 +104,7 @@ tincd_SOURCES = \
|
|||
tinc_SOURCES = \
|
||||
dropin.c dropin.h \
|
||||
fsck.c fsck.h \
|
||||
ifconfig.c ifconfig.h \
|
||||
info.c info.h \
|
||||
invitation.c invitation.h \
|
||||
list.c list.h \
|
||||
|
|
161
src/ifconfig.c
Normal file
161
src/ifconfig.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
ifconfig.c -- Generate platform specific interface configuration commands
|
||||
Copyright (C) 2016 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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include "conf.h"
|
||||
#include "ifconfig.h"
|
||||
#include "subnet.h"
|
||||
|
||||
static long start;
|
||||
|
||||
#ifndef HAVE_MINGW
|
||||
void ifconfig_header(FILE *out) {
|
||||
fprintf(out, "#!/bin/sh\n");
|
||||
start = ftell(out);
|
||||
}
|
||||
|
||||
void ifconfig_dhcp(FILE *out) {
|
||||
fprintf(out, "dhclient -nw \"$INTERFACE\"\n");
|
||||
}
|
||||
|
||||
void ifconfig_dhcp6(FILE *out) {
|
||||
fprintf(out, "dhclient -6 -nw \"$INTERFACE\"\n");
|
||||
}
|
||||
|
||||
void ifconfig_slaac(FILE *out) {
|
||||
#ifdef HAVE_LINUX
|
||||
fprintf(out, "echo 1 >\"/proc/sys/net/ipv6/conf/$INTERFACE/accept_ra\"\n");
|
||||
fprintf(out, "echo 1 >\"/proc/sys/net/ipv6/conf/$INTERFACE/autoconf\"\n");
|
||||
#else
|
||||
fprintf(out, "rtsol \"$INTERFACE\" &\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ifconfig_footer(FILE *out) {
|
||||
if(ftell(out) == start) {
|
||||
fprintf(out, "echo 'Unconfigured tinc-up script, please edit '$0'!'\n\n#ifconfig $INTERFACE <your vpn IP address> netmask <netmask of whole VPN>\n");
|
||||
return false;
|
||||
} else {
|
||||
#ifdef HAVE_LINUX
|
||||
fprintf(out, "ip link set \"$INTERFACE\" up\n");
|
||||
#else
|
||||
fprintf(out, "ifconfig \"$INTERFACE\" up\n");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void ifconfig_header(FILE *out) {
|
||||
start = ftell(out);
|
||||
}
|
||||
|
||||
void ifconfig_dhcp(FILE *out) {
|
||||
fprintf(out, "netsh interface ipv4 set address \"%INTERFACE%\" dhcp\n");
|
||||
}
|
||||
|
||||
void ifconfig_dhcp6(FILE *out) {
|
||||
fprintf(stderr, "DHCPv6 requested, but not supported by tinc on this platform\n");
|
||||
}
|
||||
|
||||
void ifconfig_slaac(FILE *out) {
|
||||
// It's the default?
|
||||
}
|
||||
|
||||
bool ifconfig_footer(FILE *out) {
|
||||
return ftell(out) != start;
|
||||
}
|
||||
#endif
|
||||
|
||||
static subnet_t ipv4, ipv6;
|
||||
|
||||
void ifconfig_address(FILE *out, const char *value) {
|
||||
subnet_t subnet = {};
|
||||
char str[MAXNETSTR];
|
||||
if(!str2net(&subnet, value) || !net2str(str, sizeof str, &subnet)) {
|
||||
fprintf(stderr, "Could not parse Ifconfig statement\n");
|
||||
return;
|
||||
}
|
||||
switch(subnet.type) {
|
||||
case SUBNET_IPV4: ipv4 = subnet; break;
|
||||
case SUBNET_IPV6: ipv6 = subnet; break;
|
||||
}
|
||||
#if defined(HAVE_LINUX)
|
||||
switch(subnet.type) {
|
||||
case SUBNET_MAC: fprintf(out, "ip link set \"$INTERFACE\" address %s\n", str); break;
|
||||
case SUBNET_IPV4: fprintf(out, "ip addr replace %s dev \"$INTERFACE\"\n", str); break;
|
||||
case SUBNET_IPV6: fprintf(out, "ip addr replace %s dev \"$INTERFACE\"\n", str); break;
|
||||
}
|
||||
#elif defined(HAVE_BSD)
|
||||
switch(subnet.type) {
|
||||
case SUBNET_MAC: fprintf(out, "ifconfig \"$INTERFACE\" link %s\n", str); break;
|
||||
case SUBNET_IPV4: fprintf(out, "ifconfig \"$INTERFACE\" %s\n", str); break;
|
||||
case SUBNET_IPV6: fprintf(out, "ifconfig \"$INTERFACE\" inet6 %s\n", str); break;
|
||||
}
|
||||
#elif defined(HAVE_MINGW) || defined(HAVE_CYGWIN)
|
||||
switch(subnet.type) {
|
||||
case SUBNET_MAC: fprintf(out, "ip link set \"$INTERFACE\" address %s\n", str); break;
|
||||
case SUBNET_IPV4: fprintf(out, "netsh inetface ipv4 set address \"$INTERFACE\" static %s\n", str); break;
|
||||
case SUBNET_IPV6: fprintf(out, "netsh inetface ipv6 set address \"$INTERFACE\" static %s\n", str); break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ifconfig_route(FILE *out, const char *value) {
|
||||
subnet_t subnet = {};
|
||||
char str[MAXNETSTR];
|
||||
if(!str2net(&subnet, value) || !net2str(str, sizeof str, &subnet) || subnet.type == SUBNET_MAC) {
|
||||
fprintf(stderr, "Could not parse Ifconfig statement\n");
|
||||
return;
|
||||
}
|
||||
#if defined(HAVE_LINUX)
|
||||
switch(subnet.type) {
|
||||
case SUBNET_IPV4: fprintf(out, "ip route add %s dev \"$INTERFACE\"\n", str); break;
|
||||
case SUBNET_IPV6: fprintf(out, "ip route add %s dev \"$INTERFACE\"\n", str); break;
|
||||
}
|
||||
#elif defined(HAVE_BSD)
|
||||
// BSD route command is silly and doesn't accept an interface name as a destination.
|
||||
char gwstr[MAXNETSTR] = "";
|
||||
switch(subnet.type) {
|
||||
case SUBNET_IPV4:
|
||||
if(!ipv4.type) {
|
||||
fprintf(stderr, "Route requested but no Ifconfig\n");
|
||||
return;
|
||||
}
|
||||
net2str(gwstr, sizeof gwstr, &ipv4);
|
||||
char *p = strchr(gwstr, '/'); if(p) *p = 0;
|
||||
fprintf(out, "route add %s %s\n", str, gwstr);
|
||||
break;
|
||||
case SUBNET_IPV6:
|
||||
if(!ipv6.type) {
|
||||
fprintf(stderr, "Route requested but no Ifconfig\n");
|
||||
return;
|
||||
}
|
||||
net2str(gwstr, sizeof gwstr, &ipv6);
|
||||
char *p = strchr(gwstr, '/'); if(p) *p = 0;
|
||||
fprintf(out, "route add -inet6 %s %s\n", str, gwstr);
|
||||
break;
|
||||
}
|
||||
#elif defined(HAVE_MINGW) || defined(HAVE_CYGWIN)
|
||||
switch(subnet.type) {
|
||||
case SUBNET_IPV4: fprintf(out, "netsh inetface ipv4 add route %s \"$INTERFACE\"\n", str); break;
|
||||
case SUBNET_IPV6: fprintf(out, "netsh inetface ipv6 add route %s \"$INTERFACE\"\n", str); break;
|
||||
}
|
||||
#endif
|
||||
}
|
31
src/ifconfig.h
Normal file
31
src/ifconfig.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
ifconfig.h -- header for ifconfig.c.
|
||||
Copyright (C) 2016 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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __TINC_IFCONFIG_H__
|
||||
#define __TINC_IFCONFIG_H__
|
||||
|
||||
extern void ifconfig_dhcp(FILE *out);
|
||||
extern void ifconfig_dhcp6(FILE *out);
|
||||
extern void ifconfig_slaac(FILE *out);
|
||||
extern void ifconfig_address(FILE *out, const char *value);
|
||||
extern void ifconfig_route(FILE *out, const char *value);
|
||||
extern void ifconfig_header(FILE *out);
|
||||
extern bool ifconfig_footer(FILE *out);
|
||||
|
||||
#endif
|
|
@ -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"
|
||||
|
@ -602,7 +604,20 @@ make_names:
|
|||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fprintf(fup, "#!/bin/sh\n");
|
||||
long fuppos = ftell(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;
|
||||
|
@ -641,6 +656,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);
|
||||
|
@ -655,6 +688,8 @@ make_names:
|
|||
}
|
||||
|
||||
fclose(f);
|
||||
bool valid_tinc_up = ifconfig_footer(fup);
|
||||
fclose(fup);
|
||||
|
||||
while(l && !strcasecmp(l, "Name")) {
|
||||
if(!check_id(value)) {
|
||||
|
@ -769,6 +804,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;
|
||||
|
|
Loading…
Reference in a new issue