Debian):
# adduser --system --no-create-home secnet
+If you're using the 'soft routes' feature (for some classes of mobile
+device) you'll have to run as root all the time, to enable secnet to
+add and remove routes from your kernel's routing table. (This
+restriction may be relaxed later if someone writes a userv service to
+modify the routing table.)
+
+If you are joining an existing VPN, read that VPN's documentation now.
+It may supersede the next paragraph.
+
You will need to allocate two IP addresses for use by secnet. One
will be for the tunnel interface on your tunnel endpoint machine (i.e.
the address you see in 'ifconfig' when you look at the tunnel
interface). The other will be for secnet itself. These addresses
-could possibly be allocated from the range used by your internal
-network: if you do this, you should think about providing appropriate
-proxy-ARP on the internal network interface of the machine running
-secnet (eg. add an entry net/ipv4/conf/eth_whatever/proxy_arp = 1 to
-/etc/sysctl.conf on Debian systems and run sysctl -p). Alternatively
-the addresses could be from some other range - this works well if the
-machine running secnet is the default route out of your network.
+should probably be allocated from the range used by your internal
+network: if you do this, you should provide appropriate proxy-ARP on
+the internal network interface of the machine running secnet (eg. add
+an entry net/ipv4/conf/eth_whatever/proxy_arp = 1 to /etc/sysctl.conf
+on Debian systems and run sysctl -p). Alternatively the addresses
+could be from some other range - this works well if the machine
+running secnet is the default route out of your network - but this
+requires more thought.
http://www.ucam.org/cam-grin/ may be useful.
Generate a site file fragment for your site (see below), and submit it
for inclusion in your VPN's 'sites' file. Download the vpn-sites file
to /etc/secnet/sites - MAKE SURE YOU GET AN AUTHENTIC COPY because the
-sites file contains public keys for all the sites in the VPN.
+sites file contains public keys for all the sites in the VPN. Use the
+make-secnet-sites.py program provided with the secnet distribution to
+convert the distributed sites file into one that can be included in a
+secnet configuration file:
+
+# make-secnet-sites.py sites sites.conf
* Configuration
You need the following information:
-1. a short name for your site, eg. "greenend". This is used to
-identify your site in the vpn-sites file.
+1. the name of your VPN.
-2. the name your site will use in the key setup protocol,
-eg. "greenend" (these two will usually be similar or the same).
+2. the name of your location(s).
-3. the DNS name of the machine that will be the "front-end" for your
+3. a short name for your site, eg. "sinister". This is used to
+identify your site in the vpn-sites file, and should probably be the
+same as its hostname.
+
+4. the DNS name of the machine that will be the "front-end" for your
secnet installation. This will typically be the name of the gateway
machine for your network, eg. sinister.dynamic.greenend.org.uk
machine can be configured to forward UDP packets to the machine that
is running secnet.
-4. the port number used to contact secnet at your site. This is the
+5. the port number used to contact secnet at your site. This is the
port number on the front-end machine, and does not necessarily have to
-match the port number on the machine running secnet.
+match the port number on the machine running secnet. If you want to
+use a privileged port number we suggest 410. An appropriate
+unprivileged port number is 51396. (These numbers were picked at
+random.)
-5. the list of networks accessible at your site over the VPN.
+6. the list of networks accessible at your site over the VPN.
-6. the public part of the RSA key you generated during installation
+7. the public part of the RSA key you generated during installation
(in /etc/secnet/key.pub if you followed the installation
instructions). This file contains three numbers and a comment on one
-line. The first number is the key length in bits, and should be
-ignored. The second number (typically small) is the encryption key
-'e', and the third number (large) is the modulus 'n'.
+line.
If you are running secnet on a particularly slow machine, you may like
to specify a larger value for the key setup retry timeout than the
The site file fragment should look something like this:
-shortname {
- name "sitename";
- address "your.public.address.org.uk";
- port 5678;
- networks "172.18.45.0/24";
- key rsa-public("35","153279875126380522437827076871354104097683702803616313419670959273217685015951590424876274370401136371563604396779864283483623325238228723798087715987495590765759771552692972297669972616769731553560605291312242789575053620182470998166393580503400960149506261455420521811814445675652857085993458063584337404329");
- };
-
-See 'example-sites-file' for more examples.
+vpn sgo
+location greenend
+contact steve@greenend.org.uk
+site sinister
+ networks 192.168.73.0/24 192.168.1.0/24 172.19.71.0/24
+ address sinister.dynamic.greenend.org.uk 51396
+ pubkey 1024 35 142982503......[lots more].....0611 steve@sinister
.PHONY: all clean realclean dist install
PACKAGE:=secnet
-VERSION:=0.1.4
+VERSION:=0.1.5
@SET_MAKE@
pubkey x y z: x=keylen, y=encryption key, z=modulus
mobile: declare this to be a 'mobile' site
+** Logging etc.
+
+There are several possible ways of running secnet:
+
+'reporting' only: --version, --help, etc. command line options and the
+--just-check-config mode.
+
+'normal' run: perform setup in the foreground, and then background.
+
+'failed' run: setup in the foreground, and terminate with an error
+before going to background.
+
+'reporting' modes should never output anything except to stdout/stderr.
+'normal' and 'failed' runs output to stdout/stderr before
+backgrounding, then thereafter output only to log destinations.
+
** Protocols
*** Protocol environment:
udp: dict argument
port (integer): UDP port to listen and send on
buffer (buffer closure): buffer for incoming packets
+ authbind (string): optional, path to authbind-helper program
** util
Defines:
logfile (closure => log closure)
+ syslog (closure => log closure)
sysbuffer (closure => buffer closure)
+logfile: dict argument
+ filename (string): where to log to
+ class (string list): what type of messages to log
+ { "debug-config", M_DEBUG_CONFIG },
+ { "debug-phase", M_DEBUG_PHASE },
+ { "debug", M_DEBUG },
+ { "all-debug", M_DEBUG|M_DEBUG_PHASE|M_DEBUG_CONFIG },
+ { "info", M_INFO },
+ { "notice", M_NOTICE },
+ { "warning", M_WARNING },
+ { "error", M_ERROR },
+ { "security", M_SECURITY },
+ { "fatal", M_FATAL },
+ { "default", M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
+ { "verbose", M_INFO|M_NOTICE|M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
+ { "quiet", M_FATAL }
+
+syslog: dict argument
+ ident (string): include this string in every log message
+ facility (string): facility to log as
+ { "authpriv", LOG_AUTHPRIV },
+ { "cron", LOG_CRON },
+ { "daemon", LOG_DAEMON },
+ { "kern", LOG_KERN },
+ { "local0", LOG_LOCAL0 },
+ { "local1", LOG_LOCAL1 },
+ { "local2", LOG_LOCAL2 },
+ { "local3", LOG_LOCAL3 },
+ { "local4", LOG_LOCAL4 },
+ { "local5", LOG_LOCAL5 },
+ { "local6", LOG_LOCAL6 },
+ { "local7", LOG_LOCAL7 },
+ { "lpr", LOG_LPR },
+ { "mail", LOG_MAIL },
+ { "news", LOG_NEWS },
+ { "syslog", LOG_SYSLOG },
+ { "user", LOG_USER },
+ { "uucp", LOG_UUCP }
+
+sysbuffer: integer[,dict]
+ arg1: buffer length
+ arg2: options:
+ lockdown (boolean): if True, mlock() the buffer
+
** site
Defines:
netlink.c: investigate why 'default' routes don't appear to work
(reported by JDA).
-slip.c: detect failure of userv-ipif to start.
+slip.c: detect failure of userv-ipif to start. Restart userv-ipif to
+cope with soft routes? Restart it if it fails in use?
tun.c: jdamery reports tun-old code works on Linux-2.2.
Unresolved problem with ioctl(TUNSETIFF) sometimes returning EINVAL, seems
file. Abandon key exchanges when a bad packet is received. Modify
protocol to include version fields, as described in the NOTES file.
-transform.c: make generic
-
-util.c: sort out logging
+transform.c: see below
sha1.c: test
mode, etc.
Signal handling! Really just cope with SIGCHLD and SIGTERM. Possibly
-use SIGUSR1/2 for prodding things.
-
-Write scripts to generate the 'real' sites file from a less-expressive
-version that's more easily checked by external tools.
+use SIGUSR1/2 for prodding things. Manage child processes properly.
return NULL;
}
+uint32_t list_length(list_t *a)
+{
+ uint32_t l=0;
+ list_t *i;
+ for (i=a; i; i=i->next) l++;
+ return l;
+}
+
list_t *list_copy(list_t *a)
{
list_t *r, *i, *b, *l;
}
}
+uint32_t string_to_word(string_t s, struct cloc loc,
+ struct flagstr *f, string_t desc)
+{
+ struct flagstr *j;
+ for (j=f; j->name; j++)
+ if (strcmp(s,j->name)==0)
+ return j->value;
+ cfgfatal(loc,desc,"option \"%s\" not known\n",s);
+ return 0;
+}
+
uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
{
list_t *i;
"strings\n");
}
for (j=f; j->name; j++)
- if (strcmp(i->item->data.string,j->name)==0)
- r|=j->value;
+ r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
}
return r;
}
-secnet (0.1.2-1) unstable; urgency=low
+secnet (0.1.5-1) unstable; urgency=low
* New upstream version.
dh_testroot
# dh_installdebconf
dh_installdocs INSTALL README NOTES TODO
- dh_installexamples example.conf example-sites-file
+ dh_installexamples example.conf make-secnet-sites.py ipaddr.py
# dh_installmenu
# dh_installlogrotate
# dh_installemacsen
# secnet example configuration file
# Log facility
-log logfile("secnet","local2"); # Not yet implemented, goes to stderr
+log syslog {
+ ident "secnet";
+ facility "local0";
+};
+
+# Alternatively you could log to a file:
+# log logfile {
+# filename "/var/log/secnet";
+# class "info","notice","warning","error","security","fatal";
+# # There are some useful message classes that could replace
+# # this list:
+# # 'default' -> warning,error,security,fatal
+# # 'verbose' -> info,notice,default
+# # 'quiet' -> fatal
+# };
# Systemwide configuration (all other configuration is per-site):
# log a log facility for program messages
import os
import ipaddr
-VERSION="0.1.4"
+VERSION="0.1.5"
class vpn:
def __init__(self,name):
class dhgroup:
def __init__(self,w):
- self.w=w
+ self.mod=w[1]
+ self.gen=w[2]
def out(self):
- return 'dh diffie-hellman("%s","%s");'%(self.w[1],self.w[2])
+ return 'dh diffie-hellman("%s","%s");'%(self.mod,self.gen)
class hash:
def __init__(self,w):
- self.w=w
- if (w[1]!='md5' and w[1]!='sha1'):
- complain("unknown hash type %s"%(w[1]))
+ self.ht=w[1]
+ if (self.ht!='md5' and self.ht!='sha1'):
+ complain("unknown hash type %s"%(self.ht))
def out(self):
- return 'hash %s;'%(self.w[1])
+ return 'hash %s;'%(self.ht)
class email:
def __init__(self,w):
- self.w=w
+ self.addr=w[1]
def out(self):
- return '# Contact email address: <%s>'%(self.w[1])
+ return '# Contact email address: <%s>'%(self.addr)
class num:
def __init__(self,w):
- self.w=w
+ self.what=w[0]
+ self.n=string.atol(w[1])
def out(self):
- return '%s %s;'%(self.w[0],self.w[1])
+ return '%s %d;'%(self.what,self.n)
class address:
def __init__(self,w):
self.w=w
+ self.adr=w[1]
+ self.port=string.atoi(w[2])
+ if (self.port<1 or self.port>65535):
+ complain("invalid port number")
def out(self):
- return 'address "%s"; port %s;'%(self.w[1],self.w[2])
+ return 'address "%s"; port %d;'%(self.adr,self.port)
class rsakey:
def __init__(self,w):
- self.w=w
+ self.l=string.atoi(w[1])
+ self.e=w[2]
+ self.n=w[3]
def out(self):
- return 'key rsa-public("%s","%s");'%(self.w[2],self.w[3])
+ return 'key rsa-public("%s","%s");'%(self.e,self.n)
class mobileoption:
def __init__(self,w):
for i in vpns.values():
w.write(" %s {\n"%(i.name))
for l in i.locations.values():
- slist=map(lambda x:"vpn-data/%s/%s/%s"%
- (i.name,l.name,x.name),
- l.sites.values())
+ tmpl="vpn-data/%s/%s/%%s"%(i.name,l.name)
+ slist=[]
+ for s in l.sites.values(): slist.append(tmpl%s.name)
w.write(" %s %s;\n"%(l.name,string.join(slist,",")))
w.write("\n all-sites %s;\n"%
string.join(i.locations.keys(),","))
&st->networks);
dict_read_subnet_list(dict, "exclude-remote-networks", False, "netlink",
loc, &st->exclude_remote_networks);
- /* local-address and secnet-address do not have to be in local-networks;
- however, they should be advertised in the 'sites' file for the
+ /* secnet-address does not have to be in local-networks;
+ however, it should be advertised in the 'sites' file for the
local site. */
- st->local_address=string_to_ipaddr(
- dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
st->secnet_address=string_to_ipaddr(
dict_find_item(dict,"secnet-address", True, "netlink", loc),"netlink");
st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
uint32_t max_end_pad;
struct subnet_list networks;
struct subnet_list exclude_remote_networks;
- uint32_t local_address; /* host interface address */
uint32_t secnet_address; /* our own address */
uint32_t mtu;
struct netlink_client *clients;
#include "util.h"
#include "conffile.h"
-/* Command-line options (possibly config-file options too) */
+/* XXX should be from autoconf */
static char *configfile="/etc/secnet/secnet.conf";
bool_t just_check_config=False;
static char *userid=NULL;
static uid_t uid=0;
-static bool_t background=True;
+bool_t background=True;
static char *pidfile=NULL;
bool_t require_root_privileges=False;
string_t require_root_privileges_explanation=NULL;
break;
case 'v':
- message_level|=M_INFO|M_WARNING|M_ERROR|M_FATAL;
+ message_level|=M_INFO|M_NOTICE|M_WARNING|M_ERROR|M_SECURITY|
+ M_FATAL;
break;
- case 'n':
- background=False;
+ case 'w':
+ message_level&=(~M_WARNING);
break;
case 'd':
- message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE;
+ message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE|M_DEBUG;
break;
case 'f':
message_level=M_FATAL;
break;
+ case 'n':
+ background=False;
+ break;
+
case 'c':
if (optarg)
configfile=safe_strdup(optarg,"config_filename");
break;
default:
- Message(M_WARNING,"secnet: Unknown getopt code %c\n",c);
+ Message(M_ERROR,"secnet: Unknown getopt code %c\n",c);
}
}
if (argc-optind != 0) {
- Message(M_WARNING,"secnet: You gave extra command line parameters, "
+ Message(M_ERROR,"secnet: You gave extra command line parameters, "
"which were ignored.\n");
}
}
list_t *l;
item_t *site;
dict_t *system;
- struct log_if *log;
struct passwd *pw;
struct cloc loc;
int i;
if (!l) {
fatal("configuration does not include a system/log facility\n");
}
- log=init_log(l);
- log->log(log->st,LOG_DEBUG,"%s: logging started",version);
+ system_log=init_log(l);
/* Who are we supposed to run as? */
userid=dict_read_string(system,"userid",False,"system",loc);
fatal("run: couldn't alloca\n");
}
+ Message(M_NOTICE,"%s: starting\n",version);
+
while (!finished) {
if (gettimeofday(&tv_now, NULL)!=0) {
fatal_perror("main loop: gettimeofday");
add_hook(PHASE_SHUTDOWN,system_phase_hook,NULL);
- /* Background now, if we're supposed to: we may be unable to write the
- pidfile if we don't. */
- if (background) {
- /* Open the pidfile before forking - that way the parent can tell
- whether it succeeds */
- if (pidfile) {
- pf=fopen(pidfile,"w");
- if (!pf) {
- fatal_perror("cannot open pidfile \"%s\"",pidfile);
- }
- } else {
- Message(M_WARNING,"secnet: no pidfile configured, but "
- "backgrounding anyway\n");
+ /* Open the pidfile for writing now: we may be unable to do so
+ once we drop privileges. */
+ if (pidfile) {
+ pf=fopen(pidfile,"w");
+ if (!pf) {
+ fatal_perror("cannot open pidfile \"%s\"",pidfile);
}
+ }
+ if (!background && pf) {
+ fprintf(pf,"%d\n",getpid());
+ fclose(pf);
+ }
+
+ /* Now drop privileges */
+ if (uid!=0) {
+ if (setuid(uid)!=0) {
+ fatal_perror("can't set uid to \"%s\"",userid);
+ }
+ }
+ if (background) {
p=fork();
if (p>0) {
if (pf) {
} else if (p==0) {
/* Child process - all done, just carry on */
if (pf) fclose(pf);
+ secnet_is_daemon=True;
} else {
/* Error */
fatal_perror("cannot fork");
exit(1);
}
- } else {
- if (pidfile) {
- pf=fopen(pidfile,"w");
- if (!pf) {
- fatal_perror("cannot open pidfile \"%s\"",pidfile);
- }
- fprintf(pf,"%d\n",getpid());
- fclose(pf);
- }
}
- /* Drop privilege now, if configured to do so */
- if (uid!=0) {
- if (setuid(uid)!=0) {
- fatal_perror("can't set uid to \"%s\"",userid);
- }
- }
}
int main(int argc, char **argv)
#include "config.h"
#include <stdlib.h>
#include <stdarg.h>
-#include <syslog.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/time.h>
extern bool_t just_check_config; /* If True then we're going to exit after
reading the configuration file */
+extern bool_t background; /* If True then we'll eventually run as a daemon */
typedef struct dict dict_t; /* Configuration dictionary */
typedef struct closure closure_t;
/* List-manipulation functions */
extern list_t *list_new(void);
+extern uint32_t list_length(list_t *a);
extern list_t *list_append(list_t *a, item_t *i);
extern list_t *list_append_list(list_t *a, list_t *b);
/* Returns an item from the list (index starts at 0), or NULL */
string_t name;
uint32_t value;
};
+extern uint32_t string_to_word(string_t s, struct cloc loc,
+ struct flagstr *f, string_t desc);
extern uint32_t string_list_to_word(list_t *l, struct flagstr *f,
string_t desc);
/***** UTILITY functions *****/
-#define M_WARNING 1
-#define M_ERROR 2
-#define M_FATAL 4
-#define M_INFO 8
-#define M_DEBUG_CONFIG 16
-#define M_DEBUG_PHASE 32
+#define M_DEBUG_CONFIG 0x001
+#define M_DEBUG_PHASE 0x002
+#define M_DEBUG 0x004
+#define M_INFO 0x008
+#define M_NOTICE 0x010
+#define M_WARNING 0x020
+#define M_ERROR 0x040
+#define M_SECURITY 0x080
+#define M_FATAL 0x100
extern void fatal(char *message, ...);
extern void fatal_perror(char *message, ...);
/* LOG interface */
-typedef void log_msg_fn(void *st, int priority, char *message, ...);
-typedef void log_vmsg_fn(void *st, int priority, char *message, va_list args);
+typedef void log_msg_fn(void *st, int class, char *message, ...);
+typedef void log_vmsg_fn(void *st, int class, char *message, va_list args);
struct log_if {
void *st;
log_msg_fn *log;
log_vmsg_fn *vlog;
};
/* (convenience function, defined in util.c) */
-extern void log(struct log_if *lf, int priority, char *message, ...);
+extern void log(struct log_if *lf, int class, char *message, ...);
/* SITE interface */
{
va_list ap;
uint8_t buf[240];
+ uint32_t class;
va_start(ap,msg);
if (event&st->log_events) {
+ switch(event) {
+ case LOG_UNEXPECTED: class=M_INFO; break;
+ case LOG_SETUP_INIT: class=M_INFO; break;
+ case LOG_SETUP_TIMEOUT: class=M_NOTICE; break;
+ case LOG_ACTIVATE_KEY: class=M_INFO; break;
+ case LOG_TIMEOUT_KEY: class=M_INFO; break;
+ case LOG_SEC: class=M_SECURITY; break;
+ case LOG_STATE: class=M_DEBUG; break;
+ case LOG_DROP: class=M_DEBUG; break;
+ case LOG_DUMP: class=M_DEBUG; break;
+ case LOG_ERROR: class=M_ERROR; break;
+ default: class=M_ERROR; break;
+ }
+
vsnprintf(buf,240,msg,ap);
- st->log->log(st->log->st,0,"%s: %s",st->tunname,buf);
+ st->log->log(st->log->st,class,"%s: %s",st->tunname,buf);
}
va_end(ap);
}
uint32_t msgtype=ntohl(*(uint32_t *)(buf->start+8));
if (st->log_events & LOG_DUMP)
- log(st->log,0,"%s: %s: %08x<-%08x: %08x:",
+ log(st->log,M_DEBUG,"%s: %s: %08x<-%08x: %08x:",
st->tunname,incoming?"incoming":"outgoing",
dest,source,msgtype);
}
site() closures for all sites including our own): refuse to
talk to ourselves */
if (strcmp(st->localname,st->remotename)==0) {
- Message(M_INFO,"site %s: local-name==name -> ignoring this site\n",
+ Message(M_DEBUG,"site %s: local-name==name -> ignoring this site\n",
st->localname);
free(st);
return NULL;
and send them to the site code. */
bool_t pending_esc;
netlink_deliver_fn *netlink_to_tunnel;
+ uint32_t local_address; /* host interface address */
};
static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
be using should already have been registered. */
addrs=safe_malloc(512,"userv_phase_hook:addrs");
- snprintf(addrs,512,"%s,%s,%d,slip",ipaddr_to_string(st->nl.local_address),
+ snprintf(addrs,512,"%s,%s,%d,slip",ipaddr_to_string(st->local_address),
ipaddr_to_string(st->nl.secnet_address),st->nl.mtu);
nets=safe_malloc(1024,"userv_phase_hook:nets");
if (!st->service_user) st->service_user="root";
if (!st->service_name) st->service_name="ipif";
st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"userv-netlink",loc);
+ st->local_address=string_to_ipaddr(
+ dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
BUF_ALLOC(st->buff,"netlink:userv_apply");
st->rxfd=-1; st->txfd=-1;
struct buffer_if *buff; /* We receive packets into here
and send them to the netlink code. */
netlink_deliver_fn *netlink_to_tunnel;
+ uint32_t local_address; /* host interface address */
};
static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
no extra headers */
if (st->interface_name)
strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
- Message(M_INFO,"%s: about to ioctl(TUNSETIFF)...\n",st->nl.name);
+ Message(M_DEBUG,"%s: about to ioctl(TUNSETIFF)...\n",st->nl.name);
if (ioctl(st->fd,TUNSETIFF,&ifr)<0) {
fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name);
}
to set the TUN device's address, and route to add routes to all
our networks. */
- hostaddr=ipaddr_to_string(st->nl.local_address);
+ hostaddr=ipaddr_to_string(st->local_address);
secnetaddr=ipaddr_to_string(st->nl.secnet_address);
snprintf(mtu,6,"%d",st->nl.mtu);
mtu[5]=0;
if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
if (!st->route_path) st->route_path="route";
st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
+ st->local_address=string_to_ipaddr(
+ dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
add_hook(PHASE_GETRESOURCES,tun_phase_hook,st);
if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
if (!st->route_path) st->route_path="route";
st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
+ st->local_address=string_to_ipaddr(
+ dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
/* Old TUN interface: the network interface name depends on which
/dev/tunX file we open. If 'interface-search' is set to true, treat
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
+#include <sys/wait.h>
#include "util.h"
static beforepoll_fn udp_beforepoll;
struct cloc loc;
uint16_t port;
int fd;
+ string_t authbind;
struct buffer_if *rbuf;
struct notify_list *notify;
};
memset(&addr, 0, sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(st->port);
- if (bind(st->fd, (struct sockaddr *)&addr, sizeof(addr))!=0) {
- fatal_perror("udp (%s:%d): bind",st->loc.file,st->loc.line);
+ if (st->authbind) {
+ pid_t c;
+ int status;
+
+ /* XXX this fork() and waitpid() business needs to be hidden
+ in some system-specific library functions. */
+ c=fork();
+ if (c==-1) {
+ fatal_perror("udp_phase_hook: fork() for authbind");
+ }
+ if (c==0) {
+ char *argv[4];
+ argv[0]=st->authbind;
+ argv[1]="00000000";
+ argv[2]=alloca(8);
+ if (!argv[2]) exit(ENOMEM);
+ sprintf(argv[2],"%04X",htons(st->port));
+ argv[3]=NULL;
+ dup2(st->fd,0);
+ execvp(st->authbind,argv);
+ exit(ENOEXEC);
+ }
+ waitpid(c,&status,0);
+ if (WEXITSTATUS(status)!=0) {
+ errno=WEXITSTATUS(status);
+ fatal_perror("udp (%s:%d): authbind",st->loc.file,st->loc.line);
+ }
+ } else {
+ if (bind(st->fd, (struct sockaddr *)&addr, sizeof(addr))!=0) {
+ fatal_perror("udp (%s:%d): bind",st->loc.file,st->loc.line);
+ }
}
register_for_poll(st,udp_beforepoll,udp_afterpoll,1,"udp");
st->port=dict_read_number(d,"port",True,"udp",st->loc,0);
st->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,"udp",st->loc);
+ st->authbind=dict_read_string(d,"authbind",False,"udp",st->loc);
add_hook(PHASE_GETRESOURCES,udp_phase_hook,st);
-/* $Log: util.c,v $
- * Revision 1.2 1996/04/14 16:34:36 sde1000
- * Added syslog support
- * mpbin/mpstring functions moved from dh.c
- *
- * Revision 1.1 1996/03/14 17:05:03 sde1000
- * Initial revision
+/*
+ * util.c
+ * - output and logging support
+ * - program lifetime support
+ * - IP address and subnet munging routines
+ * - MPI convenience functions
+ */
+/*
+ * This file is
+ * Copyright (C) 1995--2001 Stephen Early <steve@greenend.org.uk>
*
+ * It is part of secnet, which is
+ * Copyright (C) 1995--2001 Stephen Early <steve@greenend.org.uk>
+ * Copyright (C) 1998 Ross Anderson, Eli Biham, Lars Knudsen
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "secnet.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <syslog.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#include <sys/wait.h>
+#include <time.h>
#include "util.h"
#include "unaligned.h"
static char *hexdigits="0123456789abcdef";
-uint32_t message_level=M_WARNING|M_ERROR|M_FATAL;
-uint32_t syslog_level=M_WARNING|M_ERROR|M_FATAL;
+bool_t secnet_is_daemon=False;
+uint32_t message_level=M_WARNING|M_ERROR|M_SECURITY|M_FATAL;
+struct log_if *system_log=NULL;
static uint32_t current_phase=0;
struct phase_hook {
static void vMessage(uint32_t class, char *message, va_list args)
{
FILE *dest=stdout;
- if (class & message_level) {
- if (class&M_FATAL || class&M_ERROR || class&M_WARNING) {
- dest=stderr;
+#define MESSAGE_BUFLEN 1023
+ static char buff[MESSAGE_BUFLEN+1]={0,};
+ uint32_t bp;
+ char *nlp;
+
+ if (secnet_is_daemon) {
+ /* Messages go to the system log interface */
+ bp=strlen(buff);
+ vsnprintf(buff+bp,MESSAGE_BUFLEN-bp,message,args);
+ /* Each line is sent separately */
+ while ((nlp=strchr(buff,'\n'))) {
+ *nlp=0;
+ log(system_log,class,buff);
+ memmove(buff,nlp+1,strlen(nlp+1)+1);
+ }
+ } else {
+ /* Messages go to stdout/stderr */
+ if (class & message_level) {
+ if (class&M_FATAL || class&M_ERROR || class&M_WARNING) {
+ dest=stderr;
+ }
+ vfprintf(dest,message,args);
}
- vfprintf(dest,message,args);
}
-/* XXX do something about syslog output here */
-#if 0
- /* Maybe send message to syslog */
- vsprintf(buff, message, args);
- /* XXX Send each line as a separate log entry */
- log(syslog_prio[level], buff);
-#endif /* 0 */
}
void Message(uint32_t class, char *message, ...)
va_list ap;
va_start(ap,message);
-
vMessage(class,message,ap);
-
va_end(ap);
}
exit(current_phase);
}
+/* Take a list of log closures and merge them */
+struct loglist {
+ struct log_if *l;
+ struct loglist *next;
+};
+
+static void log_vmulti(void *sst, int class, char *message, va_list args)
+{
+ struct loglist *st=sst, *i;
+
+ if (secnet_is_daemon) {
+ for (i=st; i; i=i->next) {
+ i->l->vlog(i->l->st,class,message,args);
+ }
+ } else {
+ vMessage(class,message,args);
+ Message(class,"\n");
+ }
+}
+
+static void log_multi(void *st, int priority, char *message, ...)
+{
+ va_list ap;
+
+ va_start(ap,message);
+ log_vmulti(st,priority,message,ap);
+ va_end(ap);
+}
+
+struct log_if *init_log(list_t *ll)
+{
+ int i=0;
+ item_t *item;
+ closure_t *cl;
+ struct loglist *l=NULL, *n;
+ struct log_if *r;
+
+ if (list_length(ll)==1) {
+ item=list_elem(ll,0);
+ cl=item->data.closure;
+ if (cl->type!=CL_LOG) {
+ cfgfatal(item->loc,"init_log","closure is not a logger");
+ }
+ return cl->interface;
+ }
+ while ((item=list_elem(ll,i++))) {
+ if (item->type!=t_closure) {
+ cfgfatal(item->loc,"init_log","item is not a closure");
+ }
+ cl=item->data.closure;
+ if (cl->type!=CL_LOG) {
+ cfgfatal(item->loc,"init_log","closure is not a logger");
+ }
+ n=safe_malloc(sizeof(*n),"init_log");
+ n->l=cl->interface;
+ n->next=l;
+ l=n;
+ }
+ if (!l) {
+ fatal("init_log: no log");
+ }
+ r=safe_malloc(sizeof(*r), "init_log");
+ r->st=l;
+ r->log=log_multi;
+ r->vlog=log_vmulti;
+ return r;
+}
+
+struct logfile {
+ closure_t cl;
+ struct log_if ops;
+ struct cloc loc;
+ string_t logfile;
+ uint32_t level;
+ FILE *f;
+};
+
+static string_t months[]={
+ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
+
+static void logfile_vlog(void *sst, int class, char *message, va_list args)
+{
+ struct logfile *st=sst;
+ time_t t;
+ struct tm *tm;
+
+ if (secnet_is_daemon) {
+ if (class&st->level) {
+ t=time(NULL);
+ tm=localtime(&t);
+ fprintf(st->f,"%s %2d %02d:%02d:%02d ",
+ months[tm->tm_mon],tm->tm_mday,tm->tm_hour,tm->tm_min,
+ tm->tm_sec);
+ vfprintf(st->f,message,args);
+ fprintf(st->f,"\n");
+ fflush(st->f);
+ }
+ } else {
+ vMessage(class,message,args);
+ Message(class,"\n");
+ }
+}
+
+static void logfile_log(void *state, int priority, char *message, ...)
+{
+ va_list ap;
+
+ va_start(ap,message);
+ logfile_vlog(state,priority,message,ap);
+ va_end(ap);
+}
+
+static void logfile_phase_hook(void *sst, uint32_t new_phase)
+{
+ struct logfile *st=sst;
+ FILE *f;
+
+ if (background) {
+ f=fopen(st->logfile,"a");
+ if (!f) fatal_perror("logfile (%s:%d): cannot open \"%s\"",
+ st->loc.file,st->loc.line,st->logfile);
+ st->f=f;
+ }
+}
+
+static struct flagstr message_class_table[]={
+ { "debug-config", M_DEBUG_CONFIG },
+ { "debug-phase", M_DEBUG_PHASE },
+ { "debug", M_DEBUG },
+ { "all-debug", M_DEBUG|M_DEBUG_PHASE|M_DEBUG_CONFIG },
+ { "info", M_INFO },
+ { "notice", M_NOTICE },
+ { "warning", M_WARNING },
+ { "error", M_ERROR },
+ { "security", M_SECURITY },
+ { "fatal", M_FATAL },
+ { "default", M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
+ { "verbose", M_INFO|M_NOTICE|M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
+ { "quiet", M_FATAL },
+ { NULL, 0 }
+};
+
+static list_t *logfile_apply(closure_t *self, struct cloc loc, dict_t *context,
+ list_t *args)
+{
+ struct logfile *st;
+ item_t *item;
+ dict_t *dict;
+
+ /* We should defer opening the logfile until the getresources
+ phase. We should defer writing into the logfile until after we
+ become a daemon. */
+
+ st=safe_malloc(sizeof(*st),"logfile_apply");
+ st->cl.description="logfile";
+ st->cl.type=CL_LOG;
+ st->cl.apply=NULL;
+ st->cl.interface=&st->ops;
+ st->ops.st=st;
+ st->ops.log=logfile_log;
+ st->ops.vlog=logfile_vlog;
+ st->loc=loc;
+ st->f=stderr;
+
+ item=list_elem(args,0);
+ if (!item || item->type!=t_dict) {
+ cfgfatal(loc,"logfile","argument must be a dictionary\n");
+ }
+ dict=item->data.dict;
+
+ st->logfile=dict_read_string(dict,"filename",True,"logfile",loc);
+ st->level=string_list_to_word(dict_lookup(dict,"class"),
+ message_class_table,"logfile");
+
+ add_hook(PHASE_GETRESOURCES,logfile_phase_hook,st);
+
+ return new_closure(&st->cl);
+}
+
+struct syslog {
+ closure_t cl;
+ struct log_if ops;
+ string_t ident;
+ int facility;
+ bool_t open;
+};
+
+static int msgclass_to_syslogpriority(uint32_t m)
+{
+ switch (m) {
+ case M_DEBUG_CONFIG: return LOG_DEBUG;
+ case M_DEBUG_PHASE: return LOG_DEBUG;
+ case M_DEBUG: return LOG_DEBUG;
+ case M_INFO: return LOG_INFO;
+ case M_NOTICE: return LOG_NOTICE;
+ case M_WARNING: return LOG_WARNING;
+ case M_ERROR: return LOG_ERR;
+ case M_SECURITY: return LOG_CRIT;
+ case M_FATAL: return LOG_EMERG;
+ default: return LOG_NOTICE;
+ }
+}
+
+static void syslog_vlog(void *sst, int class, char *message,
+ va_list args)
+{
+ struct syslog *st=sst;
+
+ if (st->open)
+ vsyslog(msgclass_to_syslogpriority(class),message,args);
+ else {
+ vMessage(class,message,args);
+ Message(class,"\n");
+ }
+}
+
+static void syslog_log(void *sst, int priority, char *message, ...)
+{
+ va_list ap;
+
+ va_start(ap,message);
+ syslog_vlog(sst,priority,message,ap);
+ va_end(ap);
+}
+
+static struct flagstr syslog_facility_table[]={
+ { "authpriv", LOG_AUTHPRIV },
+ { "cron", LOG_CRON },
+ { "daemon", LOG_DAEMON },
+ { "kern", LOG_KERN },
+ { "local0", LOG_LOCAL0 },
+ { "local1", LOG_LOCAL1 },
+ { "local2", LOG_LOCAL2 },
+ { "local3", LOG_LOCAL3 },
+ { "local4", LOG_LOCAL4 },
+ { "local5", LOG_LOCAL5 },
+ { "local6", LOG_LOCAL6 },
+ { "local7", LOG_LOCAL7 },
+ { "lpr", LOG_LPR },
+ { "mail", LOG_MAIL },
+ { "news", LOG_NEWS },
+ { "syslog", LOG_SYSLOG },
+ { "user", LOG_USER },
+ { "uucp", LOG_UUCP },
+ { NULL, 0 }
+};
+
+static void syslog_phase_hook(void *sst, uint32_t newphase)
+{
+ struct syslog *st=sst;
+
+ if (background) {
+ openlog(st->ident,0,st->facility);
+ st->open=True;
+ }
+}
+
+static list_t *syslog_apply(closure_t *self, struct cloc loc, dict_t *context,
+ list_t *args)
+{
+ struct syslog *st;
+ dict_t *d;
+ item_t *item;
+ string_t facstr;
+
+ st=safe_malloc(sizeof(*st),"syslog_apply");
+ st->cl.description="syslog";
+ st->cl.type=CL_LOG;
+ st->cl.apply=NULL;
+ st->cl.interface=&st->ops;
+ st->ops.st=st;
+ st->ops.log=syslog_log;
+ st->ops.vlog=syslog_vlog;
+
+ item=list_elem(args,0);
+ if (!item || item->type!=t_dict)
+ cfgfatal(loc,"syslog","parameter must be a dictionary\n");
+ d=item->data.dict;
+
+ st->ident=dict_read_string(d, "ident", False, "syslog", loc);
+ facstr=dict_read_string(d, "facility", True, "syslog", loc);
+ st->facility=string_to_word(facstr,loc,
+ syslog_facility_table,"syslog");
+ st->open=False;
+ add_hook(PHASE_GETRESOURCES,syslog_phase_hook,st);
+
+ return new_closure(&st->cl);
+}
+
char *safe_strdup(char *s, char *message)
{
char *d;
return rv;
}
-/* Take a list of log closures and merge them */
-struct loglist {
- struct log_if *l;
- struct loglist *next;
-};
-
-static void log_vmulti(void *state, int priority, char *message, va_list args)
-{
- struct loglist *st=state, *i;
-
- for (i=st; i; i=i->next) {
- i->l->vlog(i->l->st,priority,message,args);
- }
-}
-
-static void log_multi(void *st, int priority, char *message, ...)
-{
- va_list ap;
-
- va_start(ap,message);
-
- log_vmulti(st,priority,message,ap);
-
- va_end(ap);
-}
-
-struct log_if *init_log(list_t *ll)
-{
- int i=0;
- item_t *item;
- closure_t *cl;
- struct loglist *l=NULL, *n;
- struct log_if *r;
-
- while ((item=list_elem(ll,i++))) {
- if (item->type!=t_closure) {
- cfgfatal(item->loc,"init_log","item is not a closure");
- }
- cl=item->data.closure;
- if (cl->type!=CL_LOG) {
- cfgfatal(item->loc,"init_log","closure is not a logger");
- }
- n=safe_malloc(sizeof(*n),"init_log");
- n->l=cl->interface;
- n->next=l;
- l=n;
- }
- if (!l) {
- fatal("init_log: none of the items in the list are loggers");
- }
- r=safe_malloc(sizeof(*r), "init_log");
- r->st=l;
- r->log=log_multi;
- r->vlog=log_vmulti;
- return r;
-}
-
-struct logfile {
- closure_t cl;
- struct log_if ops;
- FILE *f;
-};
-
-static void logfile_vlog(void *state, int priority, char *message,
- va_list args)
-{
- struct logfile *st=state;
-
- vfprintf(st->f,message,args);
- fprintf(st->f,"\n");
-}
-
-static void logfile_log(void *state, int priority, char *message, ...)
-{
- va_list ap;
-
- va_start(ap,message);
- logfile_vlog(state,priority,message,ap);
- va_end(ap);
-}
-
-static list_t *logfile_apply(closure_t *self, struct cloc loc, dict_t *context,
- list_t *data)
-{
- struct logfile *st;
-
- st=safe_malloc(sizeof(*st),"logfile_apply");
- st->cl.description="logfile";
- st->cl.type=CL_LOG;
- st->cl.apply=NULL;
- st->cl.interface=&st->ops;
- st->ops.st=st;
- st->ops.log=logfile_log;
- st->ops.vlog=logfile_vlog;
- st->f=stderr; /* XXX ignore args */
-
- return new_closure(&st->cl);
-}
-
static char *phases[NR_PHASES]={
"PHASE_INIT",
"PHASE_GETOPTS",
void util_module(dict_t *dict)
{
add_closure(dict,"logfile",logfile_apply);
+ add_closure(dict,"syslog",syslog_apply);
add_closure(dict,"sysbuffer",buffer_apply);
}
#include <gmp.h>
extern uint32_t message_level;
-extern uint32_t syslog_level;
+extern bool_t secnet_is_daemon;
+extern struct log_if *system_log;
#define BUF_ASSERT_FREE(buf) do { buffer_assert_free((buf), \
__FILE__,__LINE__); } \