From 7138d0c54cd2212439434d27cb2d6ea775c3039b Mon Sep 17 00:00:00 2001 From: Stephen Early Date: Sat, 13 Oct 2001 18:02:00 +0100 Subject: [PATCH] Import release 0.1.6 --- Makefile.in | 11 +- conffile.c | 7 +- ipaddr.c | 81 +++++++++ ipaddr.h | 18 ++ log.c | 435 ++++++++++++++++++++++++++++++++++++++++++++ modules.c | 2 + modules.h | 6 - netlink.c | 1 + netlink.h | 2 + process.c | 293 ++++++++++++++++++++++++++++++ process.h | 16 ++ secnet.c | 36 +++- secnet.h | 26 +-- site.c | 1 + tun.c | 1 + util.c | 514 +--------------------------------------------------- util.h | 6 - 17 files changed, 904 insertions(+), 552 deletions(-) create mode 100644 ipaddr.c create mode 100644 ipaddr.h create mode 100644 log.c delete mode 100644 modules.h create mode 100644 process.c create mode 100644 process.h diff --git a/Makefile.in b/Makefile.in index 4ec1a6c..85a770b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -18,7 +18,7 @@ .PHONY: all clean realclean dist install PACKAGE:=secnet -VERSION:=0.1.5 +VERSION:=0.1.6 @SET_MAKE@ @@ -45,7 +45,8 @@ TARGETS:=secnet OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \ resolver.o random.o udp.o site.o transform.o netlink.o rsa.o dh.o \ - serpent.o md5.o version.o tun.o slip.o sha1.o + serpent.o md5.o version.o tun.o slip.o sha1.o ipaddr.o log.o \ + process.o DISTFILES:=COPYING CREDITS INSTALL Makefile.in NOTES README TODO \ conffile.c conffile.fl conffile.h conffile.y \ @@ -53,8 +54,8 @@ DISTFILES:=COPYING CREDITS INSTALL Makefile.in NOTES README TODO \ config.h.in config.h.top configure \ configure.in debian dh.c \ example-sites-file example.conf make-secnet-sites.py \ - install.sh ipaddr.py linux md5.c md5.h \ - modules.c modules.h netlink.c netlink.h \ + install.sh ipaddr.c ipaddr.h ipaddr.py linux log.c md5.c md5.h \ + modules.c netlink.c netlink.h process.c process.h \ random.c resolver.c rsa.c \ secnet.c secnet.h serpent.c serpent.h serpentsboxes.h \ sha1.c site.c slip.c stamp-h.in transform.c tun.c udp.c \ @@ -99,7 +100,7 @@ secnet.c: conffile.h md5.o: md5.h serpent.o transform.o: serpent.h serpent.o: serpentsboxes.h -conffile.o: modules.h +conffile.o: ipaddr.h site.c util.c: unaligned.h conffile.yy.c: conffile.fl conffile.tab.c conffile.tab.c: conffile.y diff --git a/conffile.c b/conffile.c index 061fd7a..e9c84d3 100644 --- a/conffile.c +++ b/conffile.c @@ -12,7 +12,10 @@ #include "conffile.h" #include "conffile_internal.h" #include "util.h" -#include "modules.h" +#include "ipaddr.h" + +/* from modules.c */ +extern void init_builtin_modules(dict_t *dict); static struct cloc no_loc={"none",0}; @@ -783,7 +786,7 @@ void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required, cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key); } /* Count the items in the list */ - for (li=l; li; li=li->next) e++; + e=list_length(l); if (e==0) return; sl->entries=e; sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list"); diff --git a/ipaddr.c b/ipaddr.c new file mode 100644 index 0000000..2dc5928 --- /dev/null +++ b/ipaddr.c @@ -0,0 +1,81 @@ +#include "secnet.h" +#include + +/* This file should eventually incorporate all the functionality of + ipaddr.py */ + +bool_t subnet_match(struct subnet *s, uint32_t address) +{ + return (s->prefix==(address&s->mask)); +} + +bool_t subnet_matches_list(struct subnet_list *list, uint32_t address) +{ + uint32_t i; + for (i=0; ientries; i++) { + if (list->list[i].prefix == (address&list->list[i].mask)) return True; + } + return False; +} + +bool_t subnets_intersect(struct subnet a, struct subnet b) +{ + uint32_t mask=a.mask&b.mask; + return ((a.prefix&mask)==(b.prefix&mask)); +} + +bool_t subnet_intersects_with_list(struct subnet a, struct subnet_list *b) +{ + uint32_t i; + + for (i=0; ientries; i++) { + if (subnets_intersect(a,b->list[i])) return True; + } + return False; +} + +bool_t subnet_lists_intersect(struct subnet_list *a, struct subnet_list *b) +{ + uint32_t i; + for (i=0; ientries; i++) { + if (subnet_intersects_with_list(a->list[i],b)) return True; + } + return False; +} + +/* The string buffer must be at least 16 bytes long */ +string_t ipaddr_to_string(uint32_t addr) +{ + uint8_t a,b,c,d; + string_t s; + + s=safe_malloc(16,"ipaddr_to_string"); + a=addr>>24; + b=addr>>16; + c=addr>>8; + d=addr; + snprintf(s, 16, "%d.%d.%d.%d", a, b, c, d); + return s; +} + +string_t subnet_to_string(struct subnet *sn) +{ + uint32_t mask=sn->mask, addr=sn->prefix; + uint8_t a,b,c,d; + string_t s; + int i; + + s=safe_malloc(19,"subnet_to_string"); + a=addr>>24; + b=addr>>16; + c=addr>>8; + d=addr; + for (i=0; mask; i++) { + mask=(mask<<1); + } + if (i!=sn->len) { + fatal("subnet_to_string: invalid subnet structure!\n"); + } + snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, sn->len); + return s; +} diff --git a/ipaddr.h b/ipaddr.h new file mode 100644 index 0000000..99cd1ed --- /dev/null +++ b/ipaddr.h @@ -0,0 +1,18 @@ +#ifndef ipaddr_h +#define ipaddr_h + +/* Match an address (in HOST byte order) with a subnet list. + Returns True if matched. */ +extern bool_t subnet_match(struct subnet *s, uint32_t address); +extern bool_t subnet_matches_list(struct subnet_list *list, uint32_t address); +extern bool_t subnets_intersect(struct subnet a, struct subnet b); +extern bool_t subnet_intersects_with_list(struct subnet a, + struct subnet_list *b); +extern bool_t subnet_lists_intersect(struct subnet_list *a, + struct subnet_list *b); + + +extern string_t ipaddr_to_string(uint32_t addr); +extern string_t subnet_to_string(struct subnet *sn); + +#endif /* ipaddr_h */ diff --git a/log.c b/log.c new file mode 100644 index 0000000..0981506 --- /dev/null +++ b/log.c @@ -0,0 +1,435 @@ +#include "secnet.h" +#include +#include +#include +#include +#include +#include +#include "process.h" + +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 void vMessage(uint32_t class, char *message, va_list args) +{ + FILE *dest=stdout; +#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); + } + } +} + +void Message(uint32_t class, char *message, ...) +{ + va_list ap; + + va_start(ap,message); + vMessage(class,message,ap); + va_end(ap); +} + +static void vfatal(int status, bool_t perror, char *message, va_list args) +{ + int err; + + err=errno; + + enter_phase(PHASE_SHUTDOWN); + if (perror) { + Message(M_FATAL, "secnet fatal error: "); + vMessage(M_FATAL, message, args); + Message(M_FATAL, ": %s\n",strerror(err)); + } + else { + Message(M_FATAL, "secnet fatal error: "); + vMessage(M_FATAL,message,args); + } + exit(status); +} + +void fatal(char *message, ...) +{ + va_list args; + va_start(args,message); + vfatal(current_phase,False,message,args); + va_end(args); +} + +void fatal_status(int status, char *message, ...) +{ + va_list args; + va_start(args,message); + vfatal(status,False,message,args); + va_end(args); +} + +void fatal_perror(char *message, ...) +{ + va_list args; + va_start(args,message); + vfatal(current_phase,True,message,args); + va_end(args); +} + +void fatal_perror_status(int status, char *message, ...) +{ + va_list args; + va_start(args,message); + vfatal(status,True,message,args); + va_end(args); +} + +void cfgfatal(struct cloc loc, string_t facility, char *message, ...) +{ + va_list args; + + va_start(args,message); + + enter_phase(PHASE_SHUTDOWN); + + if (loc.file && loc.line) { + Message(M_FATAL, "config error (%s, %s:%d): ",facility,loc.file, + loc.line); + } else if (!loc.file && loc.line) { + Message(M_FATAL, "config error (%s, line %d): ",facility,loc.line); + } else { + Message(M_FATAL, "config error (%s): ",facility); + } + + vMessage(M_FATAL,message,args); + va_end(args); + 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_hup_notify(void *sst, int signum) +{ + struct logfile *st=sst; + FILE *f; + f=fopen(st->logfile,"a"); + if (!f) { + logfile_log(st,M_FATAL,"received SIGHUP, but could not reopen " + "logfile: %s",strerror(errno)); + } else { + fclose(st->f); + st->f=f; + logfile_log(st,M_INFO,"received SIGHUP"); + } +} + +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; + request_signal_notification(SIGHUP, logfile_hup_notify,st); + } +} + +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); +} + +init_module log_module; +void log_module(dict_t *dict) +{ + add_closure(dict,"logfile",logfile_apply); + add_closure(dict,"syslog",syslog_apply); +} diff --git a/modules.c b/modules.c index 79ba9a3..8658106 100644 --- a/modules.c +++ b/modules.c @@ -13,6 +13,7 @@ extern init_module md5_module; extern init_module slip_module; extern init_module tun_module; extern init_module sha1_module; +extern init_module log_module; void init_builtin_modules(dict_t *dict) { @@ -29,4 +30,5 @@ void init_builtin_modules(dict_t *dict) slip_module(dict); tun_module(dict); sha1_module(dict); + log_module(dict); } diff --git a/modules.h b/modules.h deleted file mode 100644 index dab50de..0000000 --- a/modules.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef modules_h -#define modules_h - -extern void init_builtin_modules(dict_t *dict); - -#endif /* modules_h */ diff --git a/netlink.c b/netlink.c index 022ddf1..e09eacf 100644 --- a/netlink.c +++ b/netlink.c @@ -10,6 +10,7 @@ #include "secnet.h" #include "util.h" +#include "ipaddr.h" #include "netlink.h" /* Generic IP checksum routine */ diff --git a/netlink.h b/netlink.h index 077e828..3f735f2 100644 --- a/netlink.h +++ b/netlink.h @@ -1,6 +1,8 @@ #ifndef netlink_h #define netlink_h +#include "ipaddr.h" + #define DEFAULT_BUFSIZE 2048 #define DEFAULT_MTU 1000 #define ICMP_BUFSIZE 1024 diff --git a/process.c b/process.c new file mode 100644 index 0000000..b40801b --- /dev/null +++ b/process.c @@ -0,0 +1,293 @@ +#include "secnet.h" +#include +#include +#include +#include +#include "process.h" + +/* Process handling - subprocesses, signals, etc. */ + +static bool_t signal_handling=False; +static sigset_t emptyset, fullset; +static sigset_t registered,pending; + +struct child { + pid_t pid; + string_t desc; + process_callback_fn *cb; + void *cst; + bool_t finished; + struct child *next; +}; + +static struct child *children=NULL; + +struct signotify { + int signum; + signal_notify_fn *notify; + void *cst; + struct signotify *next; +}; + +static struct signotify *sigs=NULL; + +static int spw,spr; /* file descriptors for signal notification pipe */ + +static void set_default_signals(void); + +/* Long-lived subprocesses can only be started once we've started + signal processing so that we can catch SIGCHLD for them and report + their exit status using the callback function. We block SIGCHLD + until signal processing has begun. */ +extern void makesubproc(process_entry_fn *entry, process_callback_fn *cb, + void *est, void *cst, string_t desc) +{ + struct child *c; + sigset_t sigchld; + pid_t p; + + c=safe_malloc(sizeof(*c),"makesubproc"); + c->desc=desc; + c->cb=cb; + c->cst=cst; + + if (!signal_handling) { + sigemptyset(&sigchld); + sigaddset(&sigchld,SIGCHLD); + sigprocmask(SIG_BLOCK,&sigchld,NULL); + } + p=fork(); + if (p==0) { + /* Child process */ + set_default_signals(); + sigprocmask(SIG_SETMASK,&emptyset,NULL); + entry(est); + abort(); + } else if (p==-1) { + fatal_perror("makesubproc (%s): fork",desc); + } + c->pid=p; + c->finished=False; + c->next=children; + children=c; +} + +static signal_notify_fn sigchld_handler; +static void sigchld_handler(void *st, int signum) +{ + struct child *i,*n,**p; + struct work { + pid_t pid; + process_callback_fn *cb; + void *cst; + int status; + struct work *next; + }; + struct work *w=NULL, *nw; + pid_t rv; + int status; + + for (i=children; i; i=i->next) { + rv=waitpid(i->pid,&status,WNOHANG); + if (rv==-1) { + fatal_perror("sigchld_handler: waitpid"); + } + if (rv==i->pid) { + i->finished=True; + + nw=safe_malloc(sizeof(*nw),"sigchld_handler"); + nw->pid=i->pid; + nw->cb=i->cb; + nw->cst=i->cst; + nw->status=status; + nw->next=w; + w=nw; + } + } + + /* Remove all the finished tasks from the list of children */ + for (i=children, p=&children; i; i=n) { + n=i->next; + if (i->finished) { + free(i); + *p=n; + } else { + p=&i->next; + } + } + + /* Notify as appropriate, then free the list */ + while (w) { + w->cb(w->cst,w->pid,w->status); + nw=w; + w=w->next; + free(nw); + } +} + +int sys_cmd(const char *path, char *arg, ...) +{ + va_list ap; + int rv; + pid_t c; + + va_start(ap,arg); + c=fork(); + if (c) { + /* Parent -> wait for child */ + waitpid(c,&rv,0); + } else if (c==0) { + char *args[100]; + int i; + /* Child -> exec command */ + args[0]=arg; + i=1; + while ((args[i++]=va_arg(ap,char *))); + execvp(path,args); + exit(1); + } else { + /* Error */ + fatal_perror("sys_cmd(%s,%s,...)"); + } + + va_end(ap); + return rv; +} + +static beforepoll_fn signal_beforepoll; +static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io, + int *timeout_io, const struct timeval *tv_now, + uint64_t *now) +{ + if (*nfds_io<1) { + *nfds_io=1; + return ERANGE; + } + *nfds_io=1; + fds[0].fd=spr; + fds[0].events=POLLIN; + return 0; +} + +static afterpoll_fn signal_afterpoll; +static void signal_afterpoll(void *st, struct pollfd *fds, int nfds, + const struct timeval *tv, uint64_t *now) +{ + uint8_t buf[16]; + struct signotify *n; + sigset_t todo,old; + + if (nfds && (fds->revents & POLLIN)) { + read(spr,buf,16); /* We don't actually care what we read; as + long as there was at least one byte + (which there was) we'll pick up the + signals in the pending set */ + + /* We reset 'pending' before processing any of the signals + that were pending so that we don't miss any signals that + are delivered partway-through processing (all we assume + about signal notification routines is that they handle all + the work available at their _start_ and only optionally any + work that arrives part-way through their execution). */ + sigprocmask(SIG_SETMASK,&fullset,&old); + todo=pending; + sigemptyset(&pending); + sigprocmask(SIG_SETMASK,&old,NULL); + + for (n=sigs; n; n=n->next) + if (sigismember(&todo,n->signum)) + n->notify(n->cst,n->signum); + } +} + +static void set_default_signals(void) +{ + struct signotify *n; + sigset_t done; + struct sigaction sa; + + sigemptyset(&done); + for (n=sigs; n; n=n->next) + if (!sigismember(&done,n->signum)) { + sigaddset(&done,n->signum); + sa.sa_handler=SIG_DFL; + sa.sa_mask=emptyset; + sa.sa_flags=0; + sigaction(n->signum,&sa,NULL); + } +} + +static void signal_handler(int signum) +{ + uint8_t thing=0; + sigaddset(&pending,signum); + write(spw,&thing,1); /* We don't care if this fails (i.e. the pipe + is full) because the service routine will + spot the pending signal anyway */ +} + +static void register_signal_handler(struct signotify *s) +{ + struct sigaction sa; + int rv; + + if (!signal_handling) return; + + if (sigismember(®istered,s->signum)) return; + sigaddset(®istered,s->signum); + + sa.sa_handler=signal_handler; + sa.sa_mask=fullset; + sa.sa_flags=0; + rv=sigaction(s->signum,&sa,NULL); + if (rv!=0) { + fatal_perror("register_signal_handler: sigaction(%d)",s->signum); + } +} + +void request_signal_notification(int signum, signal_notify_fn *notify, + void *cst) +{ + struct signotify *s; + sigset_t old; + + s=safe_malloc(sizeof(*s),"request_signal_notification"); + s->signum=signum; + s->notify=notify; + s->cst=cst; + s->next=sigs; + sigprocmask(SIG_SETMASK,&fullset,&old); + sigs=s; + register_signal_handler(s); + sigprocmask(SIG_SETMASK,&old,NULL); +} + +void start_signal_handling(void) +{ + int p[2]; + struct signotify *i; + + sigemptyset(&emptyset); + sigfillset(&fullset); + sigemptyset(®istered); + sigemptyset(&pending); + + if (pipe(p)!=0) { + fatal_perror("start_signal_handling: pipe"); + } + spw=p[1]; + spr=p[0]; + if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) { + fatal_perror("start_signal_handling: fcntl(O_NONBLOCK)"); + } + + register_for_poll(NULL,signal_beforepoll,signal_afterpoll,1,"signal"); + signal_handling=True; + + /* Register signal handlers for all the signals we're interested in */ + for (i=sigs; i; i=i->next) { + register_signal_handler(i); + } + + request_signal_notification(SIGCHLD,sigchld_handler,NULL); +} diff --git a/process.h b/process.h new file mode 100644 index 0000000..986fa38 --- /dev/null +++ b/process.h @@ -0,0 +1,16 @@ +#ifndef process_h +#define process_h + +#include + +typedef void process_callback_fn(void *cst, pid_t pid, int status); +typedef void process_entry_fn(void *cst); +typedef void signal_notify_fn(void *cst, int signum); + +extern void makesubproc(process_entry_fn *entry, process_callback_fn *cb, + void *est, void *cbst, string_t desc); + +extern void request_signal_notification(int signum, signal_notify_fn *notify, + void *cst); + +#endif /* process_h */ diff --git a/secnet.c b/secnet.c index cb68238..c7ce2b8 100644 --- a/secnet.c +++ b/secnet.c @@ -18,6 +18,7 @@ extern char version[]; #include "util.h" #include "conffile.h" +#include "process.h" /* XXX should be from autoconf */ static char *configfile="/etc/secnet/secnet.conf"; @@ -29,6 +30,16 @@ static char *pidfile=NULL; bool_t require_root_privileges=False; string_t require_root_privileges_explanation=NULL; +static pid_t secnet_pid; + +/* from log.c */ +extern uint32_t message_level; +extern bool_t secnet_is_daemon; +extern struct log_if *system_log; + +/* from process.c */ +extern void start_signal_handling(void); + /* Structures dealing with poll() call */ struct poll_interest { beforepoll_fn *before; @@ -251,9 +262,9 @@ static void run(void) fatal("run: couldn't alloca\n"); } - Message(M_NOTICE,"%s: starting\n",version); + Message(M_NOTICE,"%s [%d]: starting\n",version,secnet_pid); - while (!finished) { + do { if (gettimeofday(&tv_now, NULL)!=0) { fatal_perror("main loop: gettimeofday"); } @@ -282,6 +293,7 @@ static void run(void) i->nfds=nfds; } do { + if (finished) break; rv=poll(fds, idx, timeout); if (rv<0) { if (errno!=EINTR) { @@ -289,7 +301,7 @@ static void run(void) } } } while (rv<0); - } + } while (!finished); } static void droppriv(void) @@ -337,7 +349,19 @@ static void droppriv(void) exit(1); } } + secnet_pid=getpid(); +} +static signal_notify_fn finish,ignore_hup; +static void finish(void *st, int signum) +{ + finished=True; + Message(M_NOTICE,"%s [%d]: received %s\n",version,secnet_pid,(string_t)st); +} +static void ignore_hup(void *st, int signum) +{ + Message(M_INFO,"%s [%d]: received SIGHUP\n",version,secnet_pid); + return; } int main(int argc, char **argv) @@ -365,10 +389,14 @@ int main(int argc, char **argv) droppriv(); enter_phase(PHASE_RUN); + start_signal_handling(); + request_signal_notification(SIGTERM,finish,"SIGTERM"); + if (!background) request_signal_notification(SIGINT,finish,"SIGINT"); + request_signal_notification(SIGHUP,ignore_hup,NULL); run(); enter_phase(PHASE_SHUTDOWN); + Message(M_NOTICE,"%s [%d]: finished\n",version,secnet_pid); return 0; } - diff --git a/secnet.h b/secnet.h index ec630db..2535030 100644 --- a/secnet.h +++ b/secnet.h @@ -31,16 +31,6 @@ struct subnet_list { struct subnet *list; }; -/* Match an address (in HOST byte order) with a subnet list. - Returns True if matched. */ -extern bool_t subnet_match(struct subnet *s, uint32_t address); -extern bool_t subnet_matches_list(struct subnet_list *list, uint32_t address); -extern bool_t subnets_intersect(struct subnet a, struct subnet b); -extern bool_t subnet_intersects_with_list(struct subnet a, - struct subnet_list *b); -extern bool_t subnet_lists_intersect(struct subnet_list *a, - struct subnet_list *b); - /***** END of shared types *****/ /***** CONFIGURATION support *****/ @@ -137,7 +127,7 @@ extern uint32_t string_list_to_word(list_t *l, struct flagstr *f, /***** END of configuration support *****/ -/***** UTILITY functions *****/ +/***** LOG functions *****/ #define M_DEBUG_CONFIG 0x001 #define M_DEBUG_PHASE 0x002 @@ -155,13 +145,14 @@ extern void fatal_status(int status, char *message, ...); extern void fatal_perror_status(int status, char *message, ...); extern void cfgfatal(struct cloc loc, string_t facility, char *message, ...); -extern char *safe_strdup(char *string, char *message); -extern void *safe_malloc(size_t size, char *message); - extern void Message(uint32_t class, char *message, ...); -extern string_t ipaddr_to_string(uint32_t addr); -extern string_t subnet_to_string(struct subnet *sn); +/***** END of log functions *****/ + +/***** UTILITY functions *****/ + +extern char *safe_strdup(char *string, char *message); +extern void *safe_malloc(size_t size, char *message); extern int sys_cmd(const char *file, char *argc, ...); @@ -211,6 +202,9 @@ typedef void hook_fn(void *self, uint32_t newphase); bool_t add_hook(uint32_t phase, hook_fn *f, void *state); bool_t remove_hook(uint32_t phase, hook_fn *f, void *state); +extern uint32_t current_phase; +extern void enter_phase(uint32_t new_phase); + extern bool_t require_root_privileges; /* Some features (like netlink 'soft' routes) require that secnet retain root diff --git a/site.c b/site.c index da410c0..8e7a3fe 100644 --- a/site.c +++ b/site.c @@ -7,6 +7,7 @@ #include #include "util.h" +#include "ipaddr.h" #include "unaligned.h" #define SETUP_BUFFER_LEN 2048 diff --git a/tun.c b/tun.c index 01c0cb8..efcf6b6 100644 --- a/tun.c +++ b/tun.c @@ -48,6 +48,7 @@ static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds, struct tun *st=sst; int l; + if (nfds==0) return; if (fds[0].revents&POLLERR) { printf("tun_afterpoll: hup!\n"); } diff --git a/util.c b/util.c index 480f6bc..48814c1 100644 --- a/util.c +++ b/util.c @@ -32,12 +32,10 @@ #include #include #include -#include #include #include #include #include -#include #include "util.h" #include "unaligned.h" @@ -47,10 +45,7 @@ static char *hexdigits="0123456789abcdef"; -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; +uint32_t current_phase=0; struct phase_hook { hook_fn *fn; @@ -60,406 +55,6 @@ struct phase_hook { static struct phase_hook *hooks[NR_PHASES]={NULL,}; -static void vMessage(uint32_t class, char *message, va_list args) -{ - FILE *dest=stdout; -#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); - } - } -} - -void Message(uint32_t class, char *message, ...) -{ - va_list ap; - - va_start(ap,message); - vMessage(class,message,ap); - va_end(ap); -} - -static void vfatal(int status, bool_t perror, char *message, va_list args) -{ - int err; - - err=errno; - - enter_phase(PHASE_SHUTDOWN); - if (perror) { - Message(M_FATAL, "secnet fatal error: "); - vMessage(M_FATAL, message, args); - Message(M_FATAL, ": %s\n",strerror(err)); - } - else { - Message(M_FATAL, "secnet fatal error: "); - vMessage(M_FATAL,message,args); - } - exit(status); -} - -void fatal(char *message, ...) -{ - va_list args; - va_start(args,message); - vfatal(current_phase,False,message,args); - va_end(args); -} - -void fatal_status(int status, char *message, ...) -{ - va_list args; - va_start(args,message); - vfatal(status,False,message,args); - va_end(args); -} - -void fatal_perror(char *message, ...) -{ - va_list args; - va_start(args,message); - vfatal(current_phase,True,message,args); - va_end(args); -} - -void fatal_perror_status(int status, char *message, ...) -{ - va_list args; - va_start(args,message); - vfatal(status,True,message,args); - va_end(args); -} - -void cfgfatal(struct cloc loc, string_t facility, char *message, ...) -{ - va_list args; - - va_start(args,message); - - enter_phase(PHASE_SHUTDOWN); - - if (loc.file && loc.line) { - Message(M_FATAL, "config error (%s, %s:%d): ",facility,loc.file, - loc.line); - } else if (!loc.file && loc.line) { - Message(M_FATAL, "config error (%s, line %d): ",facility,loc.line); - } else { - Message(M_FATAL, "config error (%s): ",facility); - } - - vMessage(M_FATAL,message,args); - va_end(args); - 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; @@ -560,111 +155,6 @@ uint32_t write_mpbin(MP_INT *a, uint8_t *buffer, uint32_t buflen) return i; } -bool_t subnet_match(struct subnet *s, uint32_t address) -{ - return (s->prefix==(address&s->mask)); -} - -bool_t subnet_matches_list(struct subnet_list *list, uint32_t address) -{ - uint32_t i; - for (i=0; ientries; i++) { - if (list->list[i].prefix == (address&list->list[i].mask)) return True; - } - return False; -} - -bool_t subnets_intersect(struct subnet a, struct subnet b) -{ - uint32_t mask=a.mask&b.mask; - return ((a.prefix&mask)==(b.prefix&mask)); -} - -bool_t subnet_intersects_with_list(struct subnet a, struct subnet_list *b) -{ - uint32_t i; - - for (i=0; ientries; i++) { - if (subnets_intersect(a,b->list[i])) return True; - } - return False; -} - -bool_t subnet_lists_intersect(struct subnet_list *a, struct subnet_list *b) -{ - uint32_t i; - for (i=0; ientries; i++) { - if (subnet_intersects_with_list(a->list[i],b)) return True; - } - return False; -} - -/* The string buffer must be at least 16 bytes long */ -string_t ipaddr_to_string(uint32_t addr) -{ - uint8_t a,b,c,d; - string_t s; - - s=safe_malloc(16,"ipaddr_to_string"); - a=addr>>24; - b=addr>>16; - c=addr>>8; - d=addr; - snprintf(s, 16, "%d.%d.%d.%d", a, b, c, d); - return s; -} - -string_t subnet_to_string(struct subnet *sn) -{ - uint32_t mask=sn->mask, addr=sn->prefix; - uint8_t a,b,c,d; - string_t s; - int i; - - s=safe_malloc(19,"subnet_to_string"); - a=addr>>24; - b=addr>>16; - c=addr>>8; - d=addr; - for (i=0; mask; i++) { - mask=(mask<<1); - } - if (i!=sn->len) { - fatal("subnet_to_string: invalid subnet structure!\n"); - } - snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, sn->len); - return s; -} - -int sys_cmd(const char *path, char *arg, ...) -{ - va_list ap; - int rv; - pid_t c; - - va_start(ap,arg); - c=fork(); - if (c) { - /* Parent -> wait for child */ - waitpid(c,&rv,0); - } else if (c==0) { - char *args[100]; - int i; - /* Child -> exec command */ - args[0]=arg; - i=1; - while ((args[i++]=va_arg(ap,char *))); - execvp(path,args); - exit(1); - } else { - /* Error */ - fatal_perror("sys_cmd(%s,%s,...)"); - } - - va_end(ap); - return rv; -} - static char *phases[NR_PHASES]={ "PHASE_INIT", "PHASE_GETOPTS", @@ -846,7 +336,5 @@ static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context, 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 0c661a8..d272a59 100644 --- a/util.h +++ b/util.h @@ -10,10 +10,6 @@ #include "secnet.h" #include -extern uint32_t message_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__); } \ while(0) @@ -38,8 +34,6 @@ extern void *buf_unprepend(struct buffer_if *buf, uint32_t amount); extern void buf_append_string(struct buffer_if *buf, string_t s); -extern void enter_phase(uint32_t new_phase); - extern void read_mpbin(MP_INT *a, uint8_t *bin, int binsize); extern char *write_mpstring(MP_INT *a); -- 2.30.2