From: Stephen Early Date: Thu, 11 Oct 2001 17:16:00 +0000 (+0100) Subject: Import release 0.1.5 X-Git-Tag: v0.1.5 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=commitdiff_plain;h=b2a56f7c93d221607864761c590952b9a614dc9f;hp=08f344d3bdffe4bb83b47b5e2d53758ce231ebc4 Import release 0.1.5 --- diff --git a/INSTALL b/INSTALL index 8ad676f..c8d9028 100644 --- a/INSTALL +++ b/INSTALL @@ -45,17 +45,27 @@ for it to run as once it's ready to drop its privileges. Example (on 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. @@ -96,7 +106,12 @@ XXX this should eventually be worked out automatically by 'configure'.] 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 @@ -109,13 +124,15 @@ is described in the README file at the moment. 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 @@ -123,18 +140,19 @@ secnet does not actually have to run on this machine, as long as the 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 @@ -143,12 +161,10 @@ See the notes in the example configuration file for more on this. 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 diff --git a/Makefile.in b/Makefile.in index f53957e..4ec1a6c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -18,7 +18,7 @@ .PHONY: all clean realclean dist install PACKAGE:=secnet -VERSION:=0.1.4 +VERSION:=0.1.5 @SET_MAKE@ diff --git a/NOTES b/NOTES index ae65e4c..2443549 100644 --- a/NOTES +++ b/NOTES @@ -113,6 +113,22 @@ networks a b c ... 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: diff --git a/README b/README index 3504183..957fe06 100644 --- a/README +++ b/README @@ -165,13 +165,59 @@ Defines: 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: diff --git a/TODO b/TODO index e9ae871..af3a468 100644 --- a/TODO +++ b/TODO @@ -6,7 +6,8 @@ endianness problems) 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 @@ -23,9 +24,7 @@ cleanly using a table. There's still quite a lot of redundancy in this 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 @@ -35,7 +34,4 @@ user to plug in different block ciphers, invent an authenticity-only 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. diff --git a/conffile.c b/conffile.c index 5bd735c..061fd7a 100644 --- a/conffile.c +++ b/conffile.c @@ -551,6 +551,14 @@ list_t *list_new(void) 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; @@ -791,6 +799,17 @@ void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required, } } +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; @@ -803,8 +822,7 @@ uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc) "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; } diff --git a/debian/changelog b/debian/changelog index 27e885f..8a69a5e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -secnet (0.1.2-1) unstable; urgency=low +secnet (0.1.5-1) unstable; urgency=low * New upstream version. diff --git a/debian/rules b/debian/rules index 6237c9d..ca7a449 100755 --- a/debian/rules +++ b/debian/rules @@ -47,7 +47,7 @@ binary-arch: build install 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 diff --git a/example.conf b/example.conf index 0d7a546..03e5f43 100644 --- a/example.conf +++ b/example.conf @@ -1,7 +1,21 @@ # 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 diff --git a/make-secnet-sites.py b/make-secnet-sites.py index 8479aaa..cb2b9e8 100755 --- a/make-secnet-sites.py +++ b/make-secnet-sites.py @@ -55,7 +55,7 @@ import sys import os import ipaddr -VERSION="0.1.4" +VERSION="0.1.5" class vpn: def __init__(self,name): @@ -106,41 +106,49 @@ class nets: 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): @@ -294,9 +302,9 @@ def outputsites(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(),",")) diff --git a/netlink.c b/netlink.c index 708205b..022ddf1 100644 --- a/netlink.c +++ b/netlink.c @@ -659,11 +659,9 @@ netlink_deliver_fn *netlink_init(struct netlink *st, &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); diff --git a/netlink.h b/netlink.h index f114444..077e828 100644 --- a/netlink.h +++ b/netlink.h @@ -39,7 +39,6 @@ struct netlink { 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; diff --git a/secnet.c b/secnet.c index ab562f1..cb68238 100644 --- a/secnet.c +++ b/secnet.c @@ -19,12 +19,12 @@ extern char version[]; #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; @@ -95,21 +95,26 @@ static void parse_options(int argc, char **argv) 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"); @@ -125,12 +130,12 @@ static void parse_options(int argc, char **argv) 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"); } } @@ -140,7 +145,6 @@ static void setup(dict_t *config) list_t *l; item_t *site; dict_t *system; - struct log_if *log; struct passwd *pw; struct cloc loc; int i; @@ -158,8 +162,7 @@ static void setup(dict_t *config) 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); @@ -248,6 +251,8 @@ static void run(void) 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"); @@ -294,20 +299,26 @@ static void droppriv(void) 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) { @@ -319,28 +330,14 @@ static void droppriv(void) } 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) diff --git a/secnet.h b/secnet.h index c85f90f..ec630db 100644 --- a/secnet.h +++ b/secnet.h @@ -6,7 +6,6 @@ #include "config.h" #include #include -#include #include #include #include @@ -48,6 +47,7 @@ extern bool_t subnet_lists_intersect(struct subnet_list *a, 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; @@ -102,6 +102,7 @@ extern string_t *dict_keys(dict_t *dict); /* 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 */ @@ -129,6 +130,8 @@ struct flagstr { 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); @@ -136,12 +139,15 @@ extern uint32_t string_list_to_word(list_t *l, struct flagstr *f, /***** 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, ...); @@ -308,15 +314,15 @@ struct comm_if { /* 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 */ diff --git a/site.c b/site.c index 989ad3e..da410c0 100644 --- a/site.c +++ b/site.c @@ -194,12 +194,27 @@ static void slog(struct site *st, uint32_t event, string_t msg, ...) { 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); } @@ -638,7 +653,7 @@ static void dump_packet(struct site *st, struct buffer_if *buf, 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); } @@ -1150,7 +1165,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, 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; diff --git a/slip.c b/slip.c index 2928c1f..afc0528 100644 --- a/slip.c +++ b/slip.c @@ -29,6 +29,7 @@ struct userv { 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, @@ -155,7 +156,7 @@ static void userv_phase_hook(void *sst, uint32_t newphase) 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"); @@ -259,6 +260,8 @@ static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context, 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; diff --git a/tun.c b/tun.c index a36f444..01c0cb8 100644 --- a/tun.c +++ b/tun.c @@ -28,6 +28,7 @@ struct tun { 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, @@ -156,7 +157,7 @@ static void tun_phase_hook(void *sst, uint32_t newphase) 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); } @@ -174,7 +175,7 @@ static void tun_phase_hook(void *sst, uint32_t newphase) 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; @@ -232,6 +233,8 @@ static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context, 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); @@ -276,6 +279,8 @@ static list_t *tun_old_apply(closure_t *self, struct cloc loc, dict_t *context, 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 diff --git a/udp.c b/udp.c index ba9f9d6..307a67a 100644 --- a/udp.c +++ b/udp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "util.h" static beforepoll_fn udp_beforepoll; @@ -43,6 +44,7 @@ struct udp { struct cloc loc; uint16_t port; int fd; + string_t authbind; struct buffer_if *rbuf; struct notify_list *notify; }; @@ -167,8 +169,37 @@ static void udp_phase_hook(void *sst, uint32_t new_phase) 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"); @@ -201,6 +232,7 @@ static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, 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); diff --git a/util.c b/util.c index 503dfe2..480f6bc 100644 --- a/util.c +++ b/util.c @@ -1,21 +1,43 @@ -/* $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 * + * It is part of secnet, which is + * Copyright (C) 1995--2001 Stephen Early + * 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 #include #include +#include #include #include #include #include +#include #include "util.h" #include "unaligned.h" @@ -25,8 +47,9 @@ 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 { @@ -40,19 +63,30 @@ static struct phase_hook *hooks[NR_PHASES]={NULL,}; 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, ...) @@ -60,9 +94,7 @@ void Message(uint32_t class, char *message, ...) va_list ap; va_start(ap,message); - vMessage(class,message,ap); - va_end(ap); } @@ -139,6 +171,295 @@ void cfgfatal(struct cloc loc, string_t facility, char *message, ...) 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; @@ -344,105 +665,6 @@ int sys_cmd(const char *path, char *arg, ...) 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", @@ -625,5 +847,6 @@ init_module util_module; void util_module(dict_t *dict) { add_closure(dict,"logfile",logfile_apply); + add_closure(dict,"syslog",syslog_apply); add_closure(dict,"sysbuffer",buffer_apply); } diff --git a/util.h b/util.h index bb298f6..0c661a8 100644 --- a/util.h +++ b/util.h @@ -11,7 +11,8 @@ #include 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__); } \