From 2fe58dfd10216a37f1ece081f926971882de112e Mon Sep 17 00:00:00 2001 From: Stephen Early Date: Thu, 20 Sep 2001 00:24:00 +0100 Subject: [PATCH 1/1] Import release 0.03 --- INSTALL | 76 ++ Makefile.in | 52 ++ NOTES | 194 +++++ TODO | 24 + conffile.c | 776 +++++++++++++++++ conffile.fl | 101 +++ conffile.h | 12 + conffile.y | 106 +++ conffile_internal.h | 54 ++ config.h.bot | 32 + config.h.in | 90 ++ config.h.top | 26 + configure | 1924 +++++++++++++++++++++++++++++++++++++++++++ configure.in | 25 + dh.c | 151 ++++ install.sh | 119 +++ md5.c | 306 +++++++ md5.h | 40 + modules.c | 27 + modules.h | 6 + myrddin.pub | 1 + netlink.c | 467 +++++++++++ private-key | Bin 0 -> 528 bytes random.c | 83 ++ resolver.c | 125 +++ rsa.c | 338 ++++++++ secnet.c | 359 ++++++++ secnet.h | 401 +++++++++ serpent.c | 320 +++++++ serpent.h | 19 + serpentsboxes.h | 493 +++++++++++ site.c | 1161 ++++++++++++++++++++++++++ testconfig | 158 ++++ testconfigz | 153 ++++ testsites | 27 + transform.c | 342 ++++++++ udp.c | 211 +++++ util.c | 572 +++++++++++++ util.h | 53 ++ 39 files changed, 9424 insertions(+) create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 NOTES create mode 100644 TODO create mode 100644 conffile.c create mode 100644 conffile.fl create mode 100644 conffile.h create mode 100644 conffile.y create mode 100644 conffile_internal.h create mode 100644 config.h.bot create mode 100644 config.h.in create mode 100644 config.h.top create mode 100755 configure create mode 100644 configure.in create mode 100644 dh.c create mode 100755 install.sh create mode 100644 md5.c create mode 100644 md5.h create mode 100644 modules.c create mode 100644 modules.h create mode 100644 myrddin.pub create mode 100644 netlink.c create mode 100644 private-key create mode 100644 random.c create mode 100644 resolver.c create mode 100644 rsa.c create mode 100644 secnet.c create mode 100644 secnet.h create mode 100644 serpent.c create mode 100644 serpent.h create mode 100644 serpentsboxes.h create mode 100644 site.c create mode 100644 testconfig create mode 100644 testconfigz create mode 100644 testsites create mode 100644 transform.c create mode 100644 udp.c create mode 100644 util.c create mode 100644 util.h diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..88b1cdb --- /dev/null +++ b/INSTALL @@ -0,0 +1,76 @@ +INSTALLATION INSTRUCTIONS for SECNET + +Ensure that you have libgmp2-dev and adns installed (and bison and +flex, and for that matter gcc...). + +If you intend to configure secnet to obtain packets from the kernel +through userv-ipif, install and configure userv-ipif. It is part of +userv-utils, available from ftp.chiark.greenend.org.uk in +/users/ian/userv + +Then, to install secnet do + +$ ./configure +$ make +# cp secnet /usr/local/sbin/secnet +# mkdir /etc/secnet +# cp example.conf /etc/secnet/secnet.conf +# cd /etc/secnet +# ssh-keygen -f key -N "" + +(When upgrading, just install the new /usr/local/sbin/secnet; keep +your current configuration file.) + +If you intend to start secnet as root, I suggest you create an userid +for it to run as once it's ready to drop its privileges. Example: +# adduser --system --no-create-home secnet + +Generate a site file fragment for your site, and submit it for +inclusion in the vpn-sites file. Download the vpn-sites file. + +* Constructing a site file fragment + +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. + +2. the name your site will use in the key setup protocol, +eg. "greenend" (these two will usually be similar or the same). + +3. 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 + +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 +port number on the front-end machine, and does not necessarily have to +match the port number on the machine running secnet. + +5. the list of networks accessible at your site over the VPN. + +6. 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 can be +ignored. The second number (typically small) is the encryption key +'e', and the third number (large) is the modulus 'n'. + +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 +default, to prevent unnecessary retransmissions of key setup +packets. 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"); + }; diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..27b5636 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,52 @@ +.DUMMY: all clean realclean dist install + +PACKAGE:=secnet +VERSION:=0.03 + +@SET_MAKE@ + +srcdir:=@srcdir@ +VPATH:=@srcdir@ + +CFLAGS:=@CFLAGS@ @DEFS@ -DVERSION=\"$(VERSION)\" -Wall + +LDFLAGS:=@LDFLAGS@ + +LDLIBS:=@LIBS@ + +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 + +%.c: %.y + +%.yy.c: %.fl + flex -o$@ $< + +%.tab.c: %.y + bison -d $< + +RM:=@RM@ + +all: $(TARGETS) + +secnet: $(OBJECTS) + +clean: + $(RM) -f $(srcdir)/*.o $(srcdir)/*~ $(srcdir)/*.yy.c \ + $(srcdir)/*.tab.[ch] + +realclean: clean + $(RM) -f $(TARGETS) $(srcdir)/Makefile $(srcdir)/config.h \ + $(srcdir)/config.log $(srcdir)/config.status $(srcdir)/config.cache \ + $(srcdir)/Makefile.bak core + +dist: realclean + (cd .. ; ln -s $(PACKAGE) $(PACKAGE)-$(VERSION) ; tar hcf - \ + $(PACKAGE)-$(VERSION) | \ + gzip -9 > $(PACKAGE)-$(VERSION).tar.gz ; rm $(PACKAGE)-$(VERSION) ) + +conffile.yy.c: conffile.fl conffile.tab.c +conffile.tab.c: conffile.y diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..272e359 --- /dev/null +++ b/NOTES @@ -0,0 +1,194 @@ +#* Design of new, multi-subnet secnet protocol + +Like the first version, we're tunnelling IP packets inside UDP +packets. To defeat various restrictions which may be imposed on us by +network providers (like the prohibition of incoming TCP connections) +we're sticking with UDP for everything this time, including key setup. + +Other new features include being able to deal with subnets hidden +behind changing 'real' IP addresses, and the ability to choose +algorithms and keys per pair of communicating sites. + +** Configuration and structure + +The network is made up from a number of 'sites'. These are collections +of machines with private IP addresses. The new secnet code runs on +machines which have interfaces on the private site network and some +way of accessing the 'real' internet. + +Each end of a tunnel is identified by a name. Often it will be +convenient for every gateway machine to use the same name for each +tunnel endpoint, but this is not vital. Individual tunnels are +identified by their two endpoint names. + + +The configuration is held in memory as a data structure as follows: + +The root is a Dictionary. Dictionaries hold (key,value) pairs. Keys +are atoms. Values are lists, dictionaries or closures. Lists can hold +the following types: string, number. + +Closures cannot be constructed directly; they are added to the +'default' dictionary before the configuration file is read. Invocation +of a closure can return any type of value. + + +Configuration file format: the file describes a dictionary. + +key value; + +value is item[,item...] + +item can be "string", number, path (looks up in dictionary), +{dictionary}, value(value), value{dictionary}. If item is a list it +is copied into the list - we can't have lists of lists. + +A path is [/]key[\[index\]][/key[\[index\]]...], defining a lookup +from the current dictionary (or parents) or the root. If a key refers +to a list of more than one item then an index number (base 0) in +square brackets can be used to specify the list item number. + +Items of the form value1(value2) invoke executable value1 with an +argument of value2. The return value can be a string or dictionary, +but not a list. (Invocation happens after the entire configuration +file has been read.) + +Items of the form value{dict} invoke executable value with an argument +of a single-element list, containing dict. It's just syntactic sugar +for value({dict}). + + +When a key is used (rather than defined) it is looked up in the +current dictionary, and if it isn't found it is looked up in the +(lexical) parent, until the root is reached. + + + + +What sorts of crypto-related things do we need to define? + +sources of randomness +block algorithms +block cipher modes? +hash functions +padding functions +public key signature algorithms +public key crypto key stores +key setup algorithms + + +** Protocols + +*** Protocol environment: + +Each gateway machine serves a particular, well-known set of private IP +addresses (i.e. the agreement over which addresses it serves is +outside the scope of this discussion). Each gateway machine has an IP +address on the interconnecting network (usually the Internet), which +may be dynamically allocated and may change at any point. + +Each gateway knows the RSA public keys of the other gateways with +which it wishes to communicate. The mechanism by which this happens is +outside the scope of this discussion. There exists a means by which +each gateway can look up the probable IP address of any other. + +*** Protocol goals: + +The ultimate goal of the protocol is for the originating gateway +machine to be able to forward packets from its section of the private +network to the appropriate gateway machine for the destination +machine, in such a way that it can be sure that the packets are being +sent to the correct destination machine, the destination machine can +be sure that the source of the packets is the originating gateway +machine, and the contents of the packets cannot be understood other +than by the two communicating gateways. + +XXX not sure about the address-change stuff; leave it out of the first +version of the protocol. From experience, IP addresses seem to be +quite stable so the feature doesn't gain us much. + +**** Protocol sub-goal 1: establish a shared key + +Definitions: + +A is the originating gateway machine +B is the destination gateway machine +PK_A is the public RSA key of A +PK_B is the public RSA key of B +PK_A^-1 is the private RSA key of A +PK_B^-1 is the private RSA key of B +x is the fresh private DH key of A +y is the fresh private DH key of B +k is g^xy mod m +g and m are generator and modulus for Diffie-Hellman +nA is a nonce generated by A +nB is a nonce generated by B +iA is an index generated by A, to be used in packets sent from B to A +iB is an index generated by B, to be used in packets sent from A to B +i? is appropriate index for receiver + +Note that 'i' may be re-used from one session to the next, whereas 'n' +is always fresh. + +Messages: + +1) A->B: *,iA,msg1,A,B,nA + +2) B->A: iA,iB,msg2,B,A,nB,nA + +(The order of B and A reverses in alternate messages so that the same +code can be used to construct them...) + +3) A->B: {iB,iA,msg3,A,B,nA,nB,g^x mod m}_PK_A^-1 + +If message 1 was a replay then A will not generate message 3, because +it doesn't recognise nA. + +If message 2 was from an attacker then B will not generate message 4, +because it doesn't recognise nB. + +4) B->A: {iA,iB,msg4,B,A,nB,nA,g^y mod m}_PK_B^-1 + +At this point, A and B share a key, k. B must keep retransmitting +message 4 until it receives a packet encrypted using key k. + +5) A: iB,iA,msg5,(ping/msg5)_k + +6) B: iA,iB,msg6,(pong/msg6)_k + +(Note that these are encrypted using the same transform that's used +for normal traffic, so they include sequence number, MAC, etc.) + +The ping and pong messages can be used by either end of the tunnel at +any time, but using msg0 as the unencrypted message type indicator. + +**** Protocol sub-goal 2: end the use of a shared key + +7) i?,i?,msg0,(end-session/msg7,A,B)_k + +This message can be sent by either party. Once sent, k can be +forgotten. Once received and checked, k can be forgotten. No need to +retransmit or confirm reception. It is suggested that this message be +sent when a key times out, or the tunnel is forcibly terminated for +some reason. + +8) i?,i?,NAK/msg8 + +If the link-layer can't work out what to do with a packet (session has +gone away, etc.) it can transmit a NAK back to the sender. The sender +can then try to verify whether the session is alive by sending ping +packets, and forget the key if it isn't. Potential denial-of-service +if the attacker can stop the ping/pong packets getting through (the +key will be forgotten and another key setup must take place), but if +they can delete packets then we've lost anyway... + +The attacker can of course forge NAKs since they aren't protected. But +if they can only forge packets then they won't be able to stop the +ping/pong working. Trust in NAKs can be rate-limited... + +Alternative idea: if you receive a packet you can't decode, because +there's no key established, then initiate key setup... + +**** Protocol sub-goal 3: send a packet + +9) i?,i?,msg0,(send-packet/msg9,packet)_k diff --git a/TODO b/TODO new file mode 100644 index 0000000..b75c633 --- /dev/null +++ b/TODO @@ -0,0 +1,24 @@ +conffile.c: deal with line numbers from included conffiles correctly + +dh.c: change format to binary from decimal string + +netlink.c: initial implementation done, needs basic router functionality +adding. Can wait. Also support tun device. + +random.c: test + +resolver.c: done + +rsa.c: check padding type, change format to binary from decimal string + +secnet.c: done + +site.c: the site_incoming() routing could be implemented much more +cleanly using a table. There's still quite a lot of redundancy in this +file. + +transform.c: done + +udp.c: done + +util.c: sort out logging diff --git a/conffile.c b/conffile.c new file mode 100644 index 0000000..746b654 --- /dev/null +++ b/conffile.c @@ -0,0 +1,776 @@ +/* + * $Log$ + */ + +/* conffile.c - process the configuration file */ + +/* #define DUMP_PARSE_TREE */ + +#include +#include +#include + +#include "secnet.h" +#include "conffile.h" +#include "conffile_internal.h" +#include "util.h" +#include "modules.h" + +static struct cloc no_loc={"none",0}; + +struct atomlist { + struct atomlist *next; + atom_t a; +}; + +struct entry { + struct entry *next; + atom_t key; + list_t *val; +}; + +struct searchlist { + struct dict *d; + struct searchlist *next; +}; + +struct dict { + struct dict *parent; + struct searchlist *search; + struct entry *entries; + uint32_t size; +}; + +static struct atomlist *atoms=NULL; + +static void process_alist(dict_t *context, struct p_node *c); +static list_t *process_invocation(dict_t *context, struct p_node *i); + +static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key) +{ + struct entry *i; + for (i=dict->entries; i; i=i->next) { + if (key==i->key) return i->val; + } + return NULL; +} + +static list_t *dict_ilookup(dict_t *dict, atom_t key) +{ + dict_t *d; + list_t *v; + + v=dict_ilookup_primitive(dict, key); + if (v) return v; + /* Check dictionaries in search path */ +/* XXX */ + /* Check lexical parents */ + for (d=dict; d; d=d->parent) { + v=dict_ilookup_primitive(d, key); + if (v) return v; + } + return NULL; +} + +static void dict_iadd(dict_t *dict, atom_t key, list_t *val) +{ + struct entry *e; + /* XXX May want to permit redefinition of keys in the future */ + /* (although it could be very confusing) */ + if (dict_ilookup_primitive(dict, key)) { + fatal("duplicate key \"%s\" in dictionary\n",key); + } + e=safe_malloc(sizeof(*e),"dict_add"); + e->next=dict->entries; + e->key=key; + e->val=val; + dict->entries=e; + dict->size++; +} + +/***** Functions beyond this point are private to the config system *****/ + +static dict_t *dict_new(dict_t *parent) +{ + dict_t *d; + + d=safe_malloc(sizeof(*d),"dict_new"); + d->parent=parent; + d->search=NULL; + d->entries=NULL; + d->size=0; + return d; +} + +static struct p_node *node_copy(struct p_node *n) +{ + struct p_node *r; + r=safe_malloc(sizeof(*r),"node_copy"); + *r=*n; + return r; +} + +static struct p_node *list_reverse(struct p_node *list) +{ + struct p_node *rl=NULL, *i, *n; + + for (i=list; i; i=i->r) { + n=node_copy(i); + n->r=rl; + rl=n; + } + return rl; +} + +/* Since we use left-recursion in the parser for efficiency, sequences + end up "backwards" in the parse tree. Rather than have complicated + code for, eg. processing assignments in the right order, we reverse + these sequences here. */ +static void ptree_mangle(struct p_node *t) +{ + if (!t) return; + ptree_mangle(t->l); + ptree_mangle(t->r); + switch (t->type) { + case T_DICT: + /* ASSERT !t->l || t->l->type==T_ALIST */ + /* ASSERT !t->r || t->r->type==T_LISTITEM */ + t->l=list_reverse(t->l); + t->r=list_reverse(t->r); + break; + case T_ASSIGNMENT: + /* ASSERT t->l->type==T_KEY */ + /* ASSERT t->r->type==T_LISTITEM */ + t->r=list_reverse(t->r); + break; + case T_ABSPATH: + case T_RELPATH: + /* ASSERT t->l==NULL */ + /* ASSERT t->r->type==T_PATHELEM */ + t->r=list_reverse(t->r); + break; + case T_EXEC: + /* ASSERT t->l */ + /* ASSERT t->r->type==T_LISTITEM */ + t->r=list_reverse(t->r); + break; + } +} + +#ifdef DUMP_PARSE_TREE +/* Convert a node type to a string, for parse tree dump */ +static string_t ntype(uint32_t type) +{ + switch(type) { + case T_STRING: return "T_STRING"; + case T_NUMBER: return "T_NUMBER"; + case T_KEY: return "T_KEY"; + case T_ASSIGNMENT: return "T_ASSIGNMENT"; + case T_LISTITEM: return "T_LISTITEM"; + case T_EXEC: return "T_EXEC"; + case T_PATHELEM: return "T_PATHELEM"; + case T_ABSPATH: return "T_ABSPATH"; + case T_RELPATH: return "T_RELPATH"; + case T_DICT: return "T_DICT"; + case T_ALIST: return "T_ALIST"; + case T_ERROR: return "T_ERROR"; + } + return "**unknown**"; +} + +static void ptree_indent(uint32_t amount) +{ + uint32_t i; + for (i=0; itype<10) { + switch(n->type) { + case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n", + n->data.string,n->loc.file,n->loc.line); break; + case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n", + n->data.number, n->loc.file,n->loc.line); break; + case T_KEY: printf("T_KEY: %s (%s line %d)\n", + n->data.key, n->loc.file,n->loc.line); break; + default: printf("**unknown primitive type**\n"); break; + } + } else { + printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line); + ptree_indent(d); + printf(" |-"); ptree_dump(n->l, d+1); + ptree_indent(d); + printf(" +-"); ptree_dump(n->r, d+1); + } +} + +#endif /* DUMP_PARSE_TREE */ + +static dict_t *dict_find_root(dict_t *d) +{ + dict_t *i; + + for (i=d; i->parent; i=i->parent); + return i; +} + +static list_t *dict_lookup_path(dict_t *context, struct p_node *p) +{ + dict_t *i; + list_t *l; + + /* ASSERT p->type==T_PATHELEM */ + /* ASSERT p->l->type==T_KEY */ + l=dict_ilookup(context, p->l->data.key); + if (!l) { + cfgfatal(p->loc,"conffile","can't find key %s\n", + p->l->data.key); + } + + while (p->r) { + if (l->item->type != t_dict) { + cfgfatal(p->loc,"conffile","path element \"%s\" " + "is not a dictionary\n",p->l->data.key); + } + i=l->item->data.dict; /* First thing in list */ + + p=p->r; + l=dict_ilookup_primitive(i, p->l->data.key); + if (!l) { + cfgfatal(p->loc,"conffile","can't find key %s\n", + p->l->data.key); + } + } + return l; +} + +static item_t *new_item(enum types type, struct cloc loc) +{ + item_t *i; + + i=safe_malloc(sizeof(*i),"new_item"); + i->type=type; + i->loc=loc; + return i; +} + +static list_t *process_item(dict_t *context, struct p_node *i) +{ + item_t *item=NULL; + + switch (i->type) { + case T_STRING: + item=new_item(t_string, i->loc); + item->data.string=i->data.string; /* XXX maybe strcpy */ + break; + case T_NUMBER: + item=new_item(t_number, i->loc); + item->data.number=i->data.number; + break; + case T_ABSPATH: + context=dict_find_root(context); + /* falls through */ + case T_RELPATH: + return dict_lookup_path(context, i->r); + /* returns immediately */ + break; + case T_DICT: + item=new_item(t_dict, i->loc); + item->data.dict=dict_new(context); +/* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */ + process_alist(item->data.dict, i->l); + break; + case T_EXEC: + return process_invocation(context, i); + /* returns immediately */ + break; + default: +#ifdef DUMP_PARSE_TREE + ptree_dump(i,0); + fatal("process_item: invalid node type for a list item (%s)\n", + ntype(i->type)); +#else + fatal("process_item: list item has invalid node type %d - recompile " + "with DUMP_PARSE_TREE defined in conffile.c for more " + "detailed debug output",i->type); +#endif /* DUMP_PARSE_TREE */ + break; + } + return list_append(NULL,item); +} + +static list_t *process_ilist(dict_t *context, struct p_node *l) +{ + struct p_node *i; + list_t *r; + + /* ASSERT l->type==T_LISTITEM */ + + r=list_new(); + + for (i=l; i; i=i->r) { + r=list_append_list(r,process_item(context,i->l)); + } + return r; +} + +static list_t *process_invocation(dict_t *context, struct p_node *i) +{ + list_t *cll; + item_t *cl; + list_t *args; + + /* ASSERT i->type==T_EXEC */ + /* ASSERT i->r->type==T_LISTITEM */ + /* XXX it might be null too */ + cll=process_item(context,i->l); + cl=cll->item; + if (cl->type != t_closure) { + cfgfatal(i->l->loc,"conffile","only closures can be invoked\n"); + } + args=process_ilist(context, i->r); + return cl->data.closure->apply(cl->data.closure, i->loc, context, args); +} + +static void process_alist(dict_t *context, struct p_node *c) +{ + struct p_node *i; + atom_t k; + list_t *l; + + if (!c) return; /* NULL assignment lists are valid (empty dictionary) */ + + /* ASSERT c->type==T_ALIST */ + if (c->type!=T_ALIST) { + fatal("invalid node type in assignment list\n"); + } + + for (i=c; i; i=i->r) { + /* ASSERT i->l && i->l->type==T_ASSIGNMENT */ + /* ASSERT i->l->l->type==T_KEY */ + /* ASSERT i->l->r->type==T_LISTITEM */ + k=i->l->l->data.key; + l=process_ilist(context, i->l->r); + dict_iadd(context, k, l); + } +} + +/* Take a list of items; turn any dictionaries in this list into lists */ +static list_t *makelist(closure_t *self, struct cloc loc, + dict_t *context, list_t *args) +{ + list_t *r=NULL, *i; + struct entry *e; + + for (i=args; i; i=i->next) { + if (i->item->type==t_dict) { + /* Convert */ + for (e=i->item->data.dict->entries; e; e=e->next) { + r=list_append_list(r, e->val); + } + } else { + r=list_append_list(r, list_append(NULL,i->item)); + } + } + return r; +} + +/* Read a file and turn it into a string */ +static list_t *readfile(closure_t *self, struct cloc loc, + dict_t *context, list_t *args) +{ + FILE *f; + string_t filename; + long length; + item_t *r; + + r=list_elem(args,0); + if (!r) { + cfgfatal(loc,"readfile","you must supply a filename\n"); + } + if (r->type!=t_string) { + cfgfatal(loc,"readfile","filename must be a string\n"); + } + filename=r->data.string; + f=fopen(filename,"rb"); + if (!f) { + fatal_perror("readfile (%s:%d): cannot open file \"%s\"", + loc.file,loc.line, filename); + } + if (fseek(f, 0, SEEK_END)!=0) { + fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line); + } + length=ftell(f); + if (length<0) { + fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line); + } + if (fseek(f, 0, SEEK_SET)!=0) { + fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line); + } + r=new_item(t_string,loc); + r->data.string=safe_malloc(length+1,"readfile"); + if (fread(r->data.string,length,1,f)!=1) { + fatal("readfile (%s:%d): fread: could not read all of file\n", + loc.file,loc.line); + } + r->data.string[length]=0; + if (fclose(f)!=0) { + fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line); + } + return list_append(NULL,r); +} + +static dict_t *process_config(struct p_node *c) +{ + dict_t *root; + dict_t *context; + item_t *i; + list_t *false; + list_t *true; + + root=dict_new(NULL); + context=root; + + /* Predefined keys for boolean values */ + i=new_item(t_bool,no_loc); + i->data.bool=False; + false=list_append(NULL,i); + i=new_item(t_bool,no_loc); + i->data.bool=True; + true=list_append(NULL,i); + dict_add(root,"false",false); + dict_add(root,"False",false); + dict_add(root,"FALSE",false); + dict_add(root,"no",false); + dict_add(root,"No",false); + dict_add(root,"NO",false); + dict_add(root,"true",true); + dict_add(root,"True",true); + dict_add(root,"TRUE",true); + dict_add(root,"yes",true); + dict_add(root,"Yes",true); + dict_add(root,"YES",true); + + add_closure(root,"makelist",makelist); + add_closure(root,"readfile",readfile); + + init_builtin_modules(root); + + process_alist(context, c); + + return root; +} + +/***** Externally accessible functions */ + +atom_t intern(string_t s) +{ + struct atomlist *i; + + for (i=atoms; i; i=i->next) { + if (strcmp(i->a, s)==0) break; + } + + if (!i) { + /* Did't find it; create a new one */ + i=safe_malloc(sizeof(*i),"intern: alloc list entry"); + i->a=safe_strdup(s,"intern: alloc string"); + i->next=atoms; + atoms=i; + } + return i->a; +} + +list_t *dict_lookup(dict_t *dict, string_t key) +{ + return dict_ilookup(dict, intern(key)); +} + +list_t *dict_lookup_primitive(dict_t *dict, string_t key) +{ + return dict_ilookup_primitive(dict, intern(key)); +} + +void dict_add(dict_t *dict, string_t key, list_t *val) +{ + dict_iadd(dict,intern(key),val); +} + +string_t *dict_keys(dict_t *dict) +{ + atom_t *r, *j; + struct entry *i; + r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys"); + for (i=dict->entries, j=r; i; i=i->next, j++) { + *j=i->key; + } + *j=NULL; + return r; +} + + +/* List-related functions */ + +list_t *list_new(void) +{ + return NULL; +} + +list_t *list_append_list(list_t *a, list_t *b) +{ + list_t *i; + + if (!a) return b; + for (i=a; i->next; i=i->next); + i->next=b; + return a; +} + +list_t *list_append(list_t *list, item_t *item) +{ + list_t *l; + + l=safe_malloc(sizeof(*l),"list_append"); + l->item=item; + l->next=NULL; + + return list_append_list(list,l); +} + +item_t *list_elem(list_t *l, uint32_t index) +{ + if (!l) return NULL; + if (index==0) return l->item; + return list_elem(l->next, index-1); +} + +list_t *new_closure(closure_t *cl) +{ + item_t *i; + + i=new_item(t_closure,no_loc); + i->data.closure=cl; + return list_append(NULL,i); +} + +void add_closure(dict_t *dict, string_t name, apply_fn apply) +{ + closure_t *c; + c=safe_malloc(sizeof(*c),"add_closure"); + c->description=name; + c->apply=apply; + c->interface=NULL; + + dict_add(dict,name,new_closure(c)); +} + +void *find_cl_if(dict_t *dict, string_t name, uint32_t type, + bool_t fail_if_invalid, string_t desc, struct cloc loc) +{ + list_t *l; + item_t *i; + closure_t *cl; + + l=dict_lookup(dict,name); + if (!l) { + if (!fail_if_invalid) return NULL; + cfgfatal(loc,desc,"closure \"%s\" not found\n",name); + } + i=list_elem(l,0); + if (i->type!=t_closure) { + if (!fail_if_invalid) return NULL; + cfgfatal(loc,desc,"\"%s\" must be a closure\n",name); + } + cl=i->data.closure; + if (cl->type!=type) { + if (!fail_if_invalid) return NULL; + cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name); + } + return cl->interface; +} + +/* Convenience functions for modules reading configuration dictionaries */ +item_t *dict_find_item(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc) +{ + list_t *l; + item_t *i; + + l=dict_lookup(dict,key); + if (!l) { + if (!required) return NULL; + cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key); + } + i=list_elem(l,0); + return i; +} + +string_t dict_read_string(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc) +{ + item_t *i; + string_t r; + + i=dict_find_item(dict,key,required,desc,loc); + if (!i) return NULL; + if (i->type!=t_string) { + cfgfatal(loc,desc,"\"%s\" must be a string\n",key); + } + r=i->data.string; + return r; +} + +uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc, uint32_t def) +{ + item_t *i; + uint32_t r; + + i=dict_find_item(dict,key,required,desc,loc); + if (!i) return def; + if (i->type!=t_number) { + cfgfatal(loc,desc,"\"%s\" must be a number\n",key); + } + r=i->data.number; + return r; +} + +bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc, bool_t def) +{ + item_t *i; + bool_t r; + + i=dict_find_item(dict,key,required,desc,loc); + if (!i) return def; + if (i->type!=t_bool) { + cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key); + } + r=i->data.bool; + return r; +} + +static struct subnet string_to_subnet(item_t *i, string_t desc) +{ + struct subnet s; + uint32_t a, b, c, d, n; + uint32_t match; + + /* i is not guaranteed to be a string */ + if (i->type!=t_string) { + cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n"); + } + + /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are + NOT optional. The subnet mask is optional; if missing it is assumed + to be /32. */ + match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n); + if (match<4) { + cfgfatal(i->loc,desc,"\"%s\" is not a valid " + "subnet specification\n",i->data.string); + } + if (match<5) { + n=32; + } + if (a>255 || b>255 || c>255 || d>255 || n>32) { + cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string); + } + s.prefix=(a<<24)|(b<<16)|(c<<8)|(d); + s.mask=(~0UL << (32-n)); + if (s.prefix & ~s.mask) { + cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained " + "in mask\n",i->data.string); + } + return s; +} + +uint32_t string_to_ipaddr(item_t *i, string_t desc) +{ + uint32_t a, b, c, d; + uint32_t match; + + /* i is not guaranteed to be a string */ + if (i->type!=t_string) { + cfgfatal(i->loc,desc,"expecting a string (IP address)\n"); + } + + match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d); + if (match<4) { + cfgfatal(i->loc,desc,"\"%s\" is not a valid " + "IP address\n",i->data.string); + } + if (a>255 || b>255 || c>255 || d>255) { + cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string); + } + return (a<<24)|(b<<16)|(c<<8)|(d); +} + +void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc, + struct subnet_list *sl) +{ + list_t *l, *li; + item_t *i; + uint32_t e=0; + + sl->entries=0; + sl->list=NULL; + l=dict_lookup(dict, key); + if (!l) { + if (!required) return; + cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key); + } + /* Count the items in the list */ + for (li=l; li; li=li->next) e++; + if (e==0) return; + sl->entries=e; + sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list"); + e=0; + /* Fill in the list */ + for (li=l; li; li=li->next) { + i=li->item; + if (i->type!=t_string) { + cfgfatal(loc,desc,"parameter \"%s\": all elements must " + "be strings\n",key); + } + sl->list[e++]=string_to_subnet(i,desc); + } +} + +dict_t *read_conffile(char *name) +{ + FILE *conffile; + struct p_node *config; + + if (strcmp(name,"-")==0) { + conffile=stdin; + } else { + conffile=fopen(name,"r"); + if (!conffile) + fatal_perror("Cannot open configuration file \"%s\"",name); + } + config_lineno=1; + config_file=name; + config=parse_conffile(conffile); + fclose(conffile); + +#ifdef DUMP_PARSE_TREE + printf("*** config file parse tree BEFORE MANGLE\n"); + ptree_dump(config,0); +#endif /* DUMP_PARSE_TREE */ + /* The root of the configuration is a T_ALIST, which needs reversing + before we mangle because it isn't the child of a T_DICT. */ + config=list_reverse(config); + ptree_mangle(config); +#ifdef DUMP_PARSE_TREE + printf("\n\n*** config file parse tree AFTER MANGLE\n"); + ptree_dump(config,0); +#endif /* DUMP_PARSE_TREE */ + return process_config(config); +} diff --git a/conffile.fl b/conffile.fl new file mode 100644 index 0000000..d191ffc --- /dev/null +++ b/conffile.fl @@ -0,0 +1,101 @@ +/* the "incl" state is used for picking up the name of an include file */ +%x incl + +%{ +#include +#include +#include + +#include "conffile_internal.h" +#include "conffile.tab.h" +#include "util.h" + +#define MAX_INCLUDE_DEPTH 10 +YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; +int include_stack_ptr=0; + +uint32_t config_lineno=0; +string_t config_file="xxx"; + +static struct p_node *leafnode(uint32_t type) +{ + struct p_node *r; + + r=safe_malloc(sizeof(*r),"leafnode"); + r->type=type; + r->loc.file=config_file; + r->loc.line=config_lineno; + r->l=NULL; r->r=NULL; + return r; +} + +static struct p_node *keynode(atom_t key) +{ + struct p_node *r; + r=leafnode(T_KEY); + r->data.key=intern(key); + return r; +} + +static struct p_node *stringnode(string_t string) +{ + struct p_node *r; + r=leafnode(T_STRING); + string++; + string[strlen(string)-1]=0; + r->data.string=safe_strdup(string,"stringnode"); + return r; +} + +static struct p_node *numnode(string_t number) +{ + struct p_node *r; + r=leafnode(T_NUMBER); + r->data.number=atoi(number); + return r; +} + +%} + +%% +include BEGIN(incl); +[ \t]* /* eat the whitespace */ +[^ \t\n]+ { /* got the include filename */ + if (include_stack_ptr >= MAX_INCLUDE_DEPTH) { + fatal("Configuration file includes nested too deeply"); + } + include_stack[include_stack_ptr++]= + YY_CURRENT_BUFFER; + yyin=fopen(yytext,"r"); + if (!yyin) { + fatal("Can't open included file %s",yytext); + } + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + BEGIN(INITIAL); + } +<> { + if (--include_stack_ptr < 0) { + yyterminate(); + } + else { + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(include_stack[include_stack_ptr]); + } + } +\"[^\"]*\" yylval=stringnode(yytext); return TOK_STRING; + +[[:alpha:]_][[:alnum:]\-_]* yylval=keynode(yytext); return TOK_KEY; + +[[:digit:]]+ yylval=numnode(yytext); return TOK_NUMBER; + + /* Eat comments */ +\#.*\n config_lineno++; + /* Count lines */ +\n config_lineno++; + /* Eat whitespace */ +[[:blank:]\j] + + /* Return all unclaimed single characters to the parser */ +. return *yytext; + diff --git a/conffile.h b/conffile.h new file mode 100644 index 0000000..d7468a5 --- /dev/null +++ b/conffile.h @@ -0,0 +1,12 @@ +/* + * $Log$ + */ + +#ifndef conffile_h +#define conffile_h + +#include "secnet.h" + +extern dict_t *read_conffile(char *conffile); + +#endif /* conffile_h */ diff --git a/conffile.y b/conffile.y new file mode 100644 index 0000000..b6b246c --- /dev/null +++ b/conffile.y @@ -0,0 +1,106 @@ +%token TOK_STRING +%token TOK_NUMBER +%token TOK_KEY + +%start input + +%{ +#include +#include +#include +#include "secnet.h" +#include "conffile_internal.h" +#include "util.h" +#define YYERROR_VERBOSE + +static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r); + +static struct p_node *result; + +static void yyerror(char *s); + +%} + +%% + +input: assignments { result = $1; } + ; + +assignments: assignments assignment { $$=node(T_ALIST, $2, $1); } + | assignment { $$=node(T_ALIST, $1, NULL); } + ; + +searchpath: /* empty */ { $$ = NULL; } + | '<' list '>' { $$ = $2; } + ; + +dict: searchpath '{' assignments '}' + { $$ = node(T_DICT, $3, $1); } + | searchpath '{' '}' { $$ = node(T_DICT, NULL, $1); } + ; + +path: '/' pathelements { $$ = node(T_ABSPATH, NULL, $2); } + | pathelements { $$ = node(T_RELPATH, NULL, $1); } + ; + +pathelements: pathelements '/' TOK_KEY { $$ = node(T_PATHELEM, $3, $1); } + | TOK_KEY { $$ = node(T_PATHELEM, $1, NULL); } + ; + +exec: item '(' list ')' { $$ = node(T_EXEC, $1, $3); } + | item '(' ')' { $$ = node(T_EXEC, $1, NULL); } + | item dict + { $$ = node(T_EXEC, $1, node(T_LISTITEM, $2, NULL)); } + ; + +list: list ',' item { $$ = node(T_LISTITEM, $3, $1); } + | item { $$ = node(T_LISTITEM, $1, NULL); } + ; + +assignment: TOK_KEY '=' list ';' { $$ = node(T_ASSIGNMENT, $1, $3); } + | TOK_KEY list ';' { $$ = node(T_ASSIGNMENT, $1, $2); } + | error ';' { $$ = node(T_ERROR, NULL, NULL); } + | error '}' { $$ = node(T_ERROR, NULL, NULL); } + | error ')' { $$ = node(T_ERROR, NULL, NULL); } + ; + +item: TOK_STRING + | TOK_NUMBER + | path + | dict + | exec + ; + +%% + +static void yyerror(char *s) +{ + Message(M_FATAL,"config file %s line %d: %s\n",config_file, + config_lineno,s); +} + +struct p_node *parse_conffile(FILE *conffile) +{ + yyin=conffile; + if (yyparse()!=0) { + fatal("Configuration file parsing failed\n"); + } + if (yynerrs>0) { + fatal("%d error%s encountered in configuration file\n", + yynerrs,yynerrs==1?"":"s"); + } + return result; +} + +static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r) +{ + struct p_node *rv; + + rv=safe_malloc(sizeof(*rv),"p_node"); + rv->type=type; + rv->loc.file=config_file; + rv->loc.line=config_lineno; + rv->l=l; + rv->r=r; + return rv; +} diff --git a/conffile_internal.h b/conffile_internal.h new file mode 100644 index 0000000..710fd96 --- /dev/null +++ b/conffile_internal.h @@ -0,0 +1,54 @@ +/* + * $Log$ + */ + +#ifndef conffile_internal_h +#define conffile_internal_h + +#include +#include "secnet.h" + +extern FILE *yyin; + +typedef string_t atom_t; + +/* Parse tree for configuration file */ + +#define YYSTYPE struct p_node * + +#define T_STRING 1 +#define T_NUMBER 2 +#define T_KEY 3 +#define T_ASSIGNMENT 10 +#define T_LISTITEM 11 +#define T_EXEC 12 +#define T_PATHELEM 13 +#define T_ABSPATH 14 +#define T_RELPATH 15 +#define T_DICT 16 +#define T_ALIST 17 +#define T_ERROR 20 + +struct p_node { + uint32_t type; + struct cloc loc; + union { + atom_t key; + string_t string; + uint32_t number; + } data; + struct p_node *l; + struct p_node *r; +}; + +extern int yylex(void); +extern string_t config_file; +extern uint32_t config_lineno; + +/* Keys in dictionaries are 'atoms', which are constructed from strings + using this call. Atoms may be compared using '=='. */ +extern atom_t intern(string_t string); + +extern struct p_node *parse_conffile(FILE *conffile); + +#endif /* conffile_internal_h */ diff --git a/config.h.bot b/config.h.bot new file mode 100644 index 0000000..adb0c7b --- /dev/null +++ b/config.h.bot @@ -0,0 +1,32 @@ +/* -*- c -*- */ + +/* These are from config.h.bot, pasted onto the end of config.h.in. */ + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +#if 0 +/* Use the definitions: */ + +/* Give us an unsigned 32-bit data type. */ +#if SIZEOF_UNSIGNED_LONG==4 +#define UWORD32 unsigned long +#elif SIZEOF_UNSIGNED_INT==4 +#define UWORD32 unsigned int +#else +#error I do not know what to use for a UWORD32. +#endif + +/* An unsigned 8-bit data type */ +#if SIZEOF_UNSIGNED_CHAR==1 +#define UBYTE8 unsigned char +#else +#error I do not know what to use for a UBYTE8 +#endif + + +#endif /* 0 */ + + +#endif /* _CONFIG_H */ diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..a3b3cef --- /dev/null +++ b/config.h.in @@ -0,0 +1,90 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ +/*************************************************************************** + * + * Part II Project, "A secure, private IP network" + * Stephen Early + * + * + * $RCSfile$ + * + * Description: + * + * Copyright: (C) Stephen Early 1995 + * + * $Revision$ + * + * $Date$ + * + * $State$ + * + ***************************************************************************/ + +/* $Log$ + */ + +#ifndef _CONFIG_H +#define _CONFIG_H + + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define to `int' if doesn't define. */ +#undef pid_t + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if you have the adns library (-ladns). */ +#undef HAVE_LIBADNS + +/* Define if you have the fl library (-lfl). */ +#undef HAVE_LIBFL + +/* Define if you have the gmp library (-lgmp). */ +#undef HAVE_LIBGMP +/* -*- c -*- */ + +/* These are from config.h.bot, pasted onto the end of config.h.in. */ + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +#if 0 +/* Use the definitions: */ + +/* Give us an unsigned 32-bit data type. */ +#if SIZEOF_UNSIGNED_LONG==4 +#define UWORD32 unsigned long +#elif SIZEOF_UNSIGNED_INT==4 +#define UWORD32 unsigned int +#else +#error I do not know what to use for a UWORD32. +#endif + +/* An unsigned 8-bit data type */ +#if SIZEOF_UNSIGNED_CHAR==1 +#define UBYTE8 unsigned char +#else +#error I do not know what to use for a UBYTE8 +#endif + + +#endif /* 0 */ + + +#endif /* _CONFIG_H */ diff --git a/config.h.top b/config.h.top new file mode 100644 index 0000000..0cea96d --- /dev/null +++ b/config.h.top @@ -0,0 +1,26 @@ +/*************************************************************************** + * + * Part II Project, "A secure, private IP network" + * Stephen Early + * + * + * $RCSfile$ + * + * Description: + * + * Copyright: (C) Stephen Early 1995 + * + * $Revision$ + * + * $Date$ + * + * $State$ + * + ***************************************************************************/ + +/* $Log$ + */ + +#ifndef _CONFIG_H +#define _CONFIG_H + diff --git a/configure b/configure new file mode 100755 index 0000000..7a08124 --- /dev/null +++ b/configure @@ -0,0 +1,1924 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=secnet.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + +# From configure.in Id + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:538: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:567: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:597: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:648: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:680: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 691 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:696: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:722: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:727: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:755: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + +# Extract the first word of "rm", so it can be a program name with args. +set dummy rm; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:789: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_RM'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$RM" in + /*) + ac_cv_path_RM="$RM" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_RM="$RM" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_RM="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +RM="$ac_cv_path_RM" +if test -n "$RM"; then + echo "$ac_t""$RM" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:822: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:843: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:860: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:877: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:902: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:915: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:982: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1036: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +echo "configure:1089: checking for pid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1122: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking for vprintf""... $ac_c" 1>&6 +echo "configure:1155: checking for vprintf" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vprintf(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1183: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +echo "configure:1207: checking for _doprnt" >&5 +if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1235: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1260: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1314: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 +echo "configure:1335: checking whether byte ordering is bigendian" >&5 +if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_bigendian=unknown +# See if sys/param.h defines the BYTE_ORDER macro. +cat > conftest.$ac_ext < +#include +int main() { + +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif +; return 0; } +EOF +if { (eval echo configure:1353: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + # It does; now see whether it defined to BIG_ENDIAN or not. +cat > conftest.$ac_ext < +#include +int main() { + +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif +; return 0; } +EOF +if { (eval echo configure:1368: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_bigendian=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_bigendian=no +fi +rm -f conftest* +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +if test $ac_cv_c_bigendian = unknown; then +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_c_bigendian=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_c_bigendian=yes +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_c_bigendian" 1>&6 +if test $ac_cv_c_bigendian = yes; then + cat >> confdefs.h <<\EOF +#define WORDS_BIGENDIAN 1 +EOF + +fi + + +echo $ac_n "checking for mpz_init_set_str in -lgmp2""... $ac_c" 1>&6 +echo "configure:1426: checking for mpz_init_set_str in -lgmp2" >&5 +ac_lib_var=`echo gmp2'_'mpz_init_set_str | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lgmp2 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo gmp2 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for yywrap in -lfl""... $ac_c" 1>&6 +echo "configure:1473: checking for yywrap in -lfl" >&5 +ac_lib_var=`echo fl'_'yywrap | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lfl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo fl | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +echo $ac_n "checking for adns_init in -ladns""... $ac_c" 1>&6 +echo "configure:1520: checking for adns_init in -ladns" >&5 +ac_lib_var=`echo adns'_'adns_init | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ladns $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo adns | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@RM@%$RM%g +s%@CPP@%$CPP%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..6c85df9 --- /dev/null +++ b/configure.in @@ -0,0 +1,25 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(secnet.c) +AC_CONFIG_HEADER(config.h) + +AC_REVISION($Id$) + +AC_LANG_C + +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PATH_PROG(RM,rm) +AC_STDC_HEADERS +AC_PROG_INSTALL +AC_PID_T +AC_SIZE_T +AC_VPRINTF +AC_C_CONST +AC_C_BIGENDIAN + +AC_CHECK_LIB(gmp2,mpz_init_set_str) +AC_CHECK_LIB(fl,yywrap) +AC_CHECK_LIB(adns,adns_init) + +AC_OUTPUT(Makefile) diff --git a/dh.c b/dh.c new file mode 100644 index 0000000..eb9ae21 --- /dev/null +++ b/dh.c @@ -0,0 +1,151 @@ +/*************************************************************************** + * + * Part II Project, "A secure, private IP network" + * Stephen Early + * + * + * $RCSfile: dh.c,v $ + * + * Description: Diffie-Hellman implementation + * + * Copyright: (C) Stephen Early 1995 + * + * $Revision: 1.3 $ + * + * $Date: 1996/05/16 18:38:54 $ + * + * $State: Exp $ + * + ***************************************************************************/ + +/* + * $Log: dh.c,v $ + * Revision 1.3 1996/05/16 18:38:54 sde1000 + * Removed unused hexdigits variable. + * + * Revision 1.2 1996/04/14 16:33:52 sde1000 + * Moved mpbin/mpstring functions into util.c + * + * Revision 1.1 1996/04/14 16:21:47 sde1000 + * Initial revision + * + */ + +#include +#include + +#include "secnet.h" +#include "util.h" + +struct dh { + closure_t cl; + struct dh_if ops; + struct cloc loc; + MP_INT p,g; /* prime modulus and generator */ +}; + +static string_t dh_makepublic(void *sst, uint8_t *secret, uint32_t secretlen) +{ + struct dh *st=sst; + string_t r; + MP_INT a, b; /* a is secret key, b is public key */ + + mpz_init(&a); + mpz_init(&b); + + read_mpbin(&a, secret, secretlen); + + mpz_powm(&b, &st->g, &a, &st->p); + + r=write_mpstring(&b); + + mpz_clear(&a); + mpz_clear(&b); + return r; +} + +static void dh_makeshared(void *sst, uint8_t *secret, uint32_t secretlen, + string_t rempublic, uint8_t *sharedsecret, + uint32_t buflen) +{ + struct dh *st=sst; + MP_INT a, b, c; + + mpz_init(&a); + mpz_init(&b); + mpz_init(&c); + + read_mpbin(&a, secret, secretlen); + mpz_set_str(&b, rempublic, 16); + + mpz_powm(&c, &b, &a, &st->p); + + write_mpbin(&c,sharedsecret,buflen); + + mpz_clear(&a); + mpz_clear(&b); + mpz_clear(&c); +} + +static list_t *dh_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct dh *st; + string_t p,g; + item_t *i; + + st=safe_malloc(sizeof(*st),"dh_apply"); + st->cl.description="dh"; + st->cl.type=CL_DH; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.makepublic=dh_makepublic; + st->ops.makeshared=dh_makeshared; + st->loc=loc; + /* We have two string arguments: the first is the modulus, and the + second is the generator. Both are in hex. */ + i=list_elem(args,0); + if (i) { + if (i->type!=t_string) { + cfgfatal(i->loc,"diffie-hellman","first argument must be a " + "string\n"); + } + p=i->data.string; + if (mpz_init_set_str(&st->p,p,16)!=0) { + cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number " + "string\n",p); + } + } else { + cfgfatal(loc,"diffie-hellman","you must provide a prime modulus\n"); + } + + i=list_elem(args,1); + if (i) { + if (i->type!=t_string) { + cfgfatal(i->loc,"diffie-hellman","second argument must be a " + "string\n"); + } + g=i->data.string; + if (mpz_init_set_str(&st->g,g,16)!=0) { + cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number " + "string\n",g); + } + } else { + cfgfatal(loc,"diffie-hellman","you must provide a generator\n"); + } + + /* Test that the modulus is really prime */ + if (mpz_probab_prime_p(&st->p,5)==0) { + cfgfatal(loc,"diffie-hellman","modulus must be a prime\n"); + } + st->ops.len=mpz_sizeinbase(&st->p,2)/8; + + return new_closure(&st->cl); +} + +init_module dh_module; +void dh_module(dict_t *dict) +{ + add_closure(dict,"diffie-hellman",dh_apply); +} diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..0ff4b6a --- /dev/null +++ b/install.sh @@ -0,0 +1,119 @@ +#!/bin/sh + +# +# install - install a program, script, or datafile +# This comes from X11R5; it is not part of GNU. +# +# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" + +instcmd="$mvprog" +chmodcmd="" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +fi + +if [ x"$dst" = x ] +then + echo "install: no destination specified" + exit 1 +fi + + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + +if [ -d $dst ] +then + dst="$dst"/`basename $src` +fi + +# Make a temp file name in the proper directory. + +dstdir=`dirname $dst` +dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + +$doit $instcmd $src $dsttmp + +# and set any options; do chmod last to preserve setuid bits + +if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi +if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi +if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi +if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi + +# Now rename the file to the real destination. + +$doit $rmcmd $dst +$doit $mvcmd $dsttmp $dst + + +exit 0 diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..ab00d26 --- /dev/null +++ b/md5.c @@ -0,0 +1,306 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + */ + +#include /* for memcpy() */ +#include /* for stupid systems */ +#include /* for ntohl() */ + +#include "secnet.h" +#include "config.h" +#include "md5.h" + +#ifdef WORDS_BIGENDIAN +static void +byteSwap(uint32_t *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void +MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) +{ + uint32_t t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void +MD5Final(md5byte digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +MD5Transform(uint32_t buf[4], uint32_t const in[16]) +{ + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif + +static void *md5_init(void) +{ + struct MD5Context *ctx; + + ctx=safe_malloc(sizeof(*ctx),"md5_init"); + MD5Init(ctx); + + return ctx; +} + +static void md5_update(void *sst, uint8_t const *buf, uint32_t len) +{ + struct MD5Context *ctx=sst; + + MD5Update(ctx,buf,len); +} + +static void md5_final(void *sst, uint8_t *digest) +{ + struct MD5Context *ctx=sst; + + MD5Final(digest,ctx); + free(ctx); +} + +struct md5 { + closure_t cl; + struct hash_if ops; +}; + +init_module md5_module; +void md5_module(dict_t *dict) +{ + struct md5 *st; + void *ctx; + string_t testinput="12345\n"; + uint8_t expected[16]= + {0xd5,0x77,0x27,0x3f,0xf8,0x85,0xc3,0xf8, + 0x4d,0xad,0xb8,0x57,0x8b,0xb4,0x13,0x99}; + uint8_t digest[16]; + int i; + + st=safe_malloc(sizeof(*st),"netlink_module"); + st->cl.description="md5"; + st->cl.type=CL_HASH; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.len=16; + st->ops.init=md5_init; + st->ops.update=md5_update; + st->ops.final=md5_final; + + dict_add(dict,"md5",new_closure(&st->cl)); + + ctx=md5_init(); + md5_update(ctx,testinput,strlen(testinput)); + md5_final(ctx,digest); + for (i=0; i<16; i++) { + if (digest[i]!=expected[i]) { + fatal("md5 module failed self-test\n"); + } + } +} diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..4dd562c --- /dev/null +++ b/md5.h @@ -0,0 +1,40 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + */ + +#ifndef MD5_H +#define MD5_H + +#define md5byte unsigned char + +struct MD5Context { + uint32_t buf[4]; + uint32_t bytes[2]; + uint32_t in[16]; +}; + +static void MD5Init(struct MD5Context *context); +static void MD5Update(struct MD5Context *context, + md5byte const *buf, unsigned len); +static void MD5Final(unsigned char digest[16], struct MD5Context *context); +static void MD5Transform(uint32_t buf[4], uint32_t const in[16]); + +#endif /* !MD5_H */ diff --git a/modules.c b/modules.c new file mode 100644 index 0000000..0ddf017 --- /dev/null +++ b/modules.c @@ -0,0 +1,27 @@ +#include "config.h" +#include "secnet.h" + +extern init_module resolver_module; +extern init_module random_module; +extern init_module udp_module; +extern init_module util_module; +extern init_module site_module; +extern init_module transform_module; +extern init_module netlink_module; +extern init_module rsa_module; +extern init_module dh_module; +extern init_module md5_module; + +void init_builtin_modules(dict_t *dict) +{ + resolver_module(dict); + random_module(dict); + udp_module(dict); + util_module(dict); + site_module(dict); + transform_module(dict); + netlink_module(dict); + rsa_module(dict); + dh_module(dict); + md5_module(dict); +} diff --git a/modules.h b/modules.h new file mode 100644 index 0000000..dab50de --- /dev/null +++ b/modules.h @@ -0,0 +1,6 @@ +#ifndef modules_h +#define modules_h + +extern void init_builtin_modules(dict_t *dict); + +#endif /* modules_h */ diff --git a/myrddin.pub b/myrddin.pub new file mode 100644 index 0000000..6736d15 --- /dev/null +++ b/myrddin.pub @@ -0,0 +1 @@ +1024 35 154107175724781677184264293617887954015562225725852111745852699493257053099810379926047345975839848434403852210573185384327420788855664167034282567346429150999373740871227795773749618022407366186555483566435251279808390618987056868368084933125373643004284007109877210578088697520329039753099981203724057693543 steve@myrddin diff --git a/netlink.c b/netlink.c new file mode 100644 index 0000000..7db3559 --- /dev/null +++ b/netlink.c @@ -0,0 +1,467 @@ +/* User-kernel network link */ + +/* We support a variety of methods: userv-ipif, ipif on its own (when + we run as root), SLIP to a pty, an external netlink daemon. There + is a performance/security tradeoff. */ + +/* When dealing with SLIP (to a pty, or ipif) we have separate rx, tx + and client buffers. When receiving we may read() any amount, not + just whole packets. When transmitting we need to bytestuff anyway, + and may be part-way through receiving. */ + +/* Each netlink device is actually a router, with its own IP + address. We should eventually do things like decreasing the TTL and + recalculating the header checksum, generating ICMP, responding to + pings, etc. but for now we can get away without them. We should + implement this stuff no matter how we get the packets to/from the + kernel. */ + +/* This is where we have the anti-spoofing paranoia - before sending a + packet to the kernel we check that the tunnel it came over could + reasonably have produced it. */ + +#include +#include +#include +#include + +#include "secnet.h" +#include "util.h" + +#define DEFAULT_BUFSIZE 2048 + +#define SLIP_END 192 +#define SLIP_ESC 219 +#define SLIP_ESCEND 220 +#define SLIP_ESCESC 221 + +struct netlink_client { + struct subnet_list *networks; + netlink_deliver_fn *deliver; + void *dst; + struct netlink_client *next; +}; + +struct userv { + closure_t cl; + struct netlink_if ops; + uint32_t max_start_pad; + uint32_t max_end_pad; + int txfd; /* We transmit to userv */ + int rxfd; /* We receive from userv */ + struct netlink_client *clients; + string_t name; + string_t userv_path; + string_t service_user; + string_t service_name; + struct subnet_list networks; + uint32_t local_address; + uint32_t secnet_address; + uint32_t mtu; + uint32_t txbuflen; + struct buffer_if *buff; /* We unstuff received packets into here + and send them to the site code. */ + bool_t pending_esc; +}; + +static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, + int *timeout_io, const struct timeval *tv_now, + uint64_t *now) +{ + struct userv *st=sst; + *nfds_io=2; + fds[0].fd=st->txfd; + fds[0].events=POLLERR; /* Might want to pick up POLLOUT sometime */ + fds[1].fd=st->rxfd; + fds[1].events=POLLIN|POLLERR|POLLHUP; + return 0; +} + +static void process_local_packet(struct userv *st) +{ + uint32_t source,dest; + struct netlink_client *c; + + source=ntohl(*(uint32_t *)(st->buff->start+12)); + dest=ntohl(*(uint32_t *)(st->buff->start+16)); + +/* printf("process_local_packet source=%s dest=%s len=%d\n", + ipaddr_to_string(source),ipaddr_to_string(dest), + st->buff->size); */ + if (!subnet_match(&st->networks,source)) { + string_t s,d; + s=ipaddr_to_string(source); + d=ipaddr_to_string(dest); + Message(M_WARNING,"%s: outgoing packet with bad source address " + "(s=%s,d=%s)\n",st->name,s,d); + free(s); free(d); + return; + } + for (c=st->clients; c; c=c->next) { + if (subnet_match(c->networks,dest)) { + c->deliver(c->dst,c,st->buff); + BUF_ALLOC(st->buff,"netlink:process_local_packet"); + return; + } + } + if (dest==st->secnet_address) { + printf("%s: secnet received packet of len %d from %s\n",st->name, + st->buff->size,ipaddr_to_string(source)); + return; + } + { + string_t s,d; + s=ipaddr_to_string(source); + d=ipaddr_to_string(dest); + Message(M_WARNING,"%s: outgoing packet with bad destination address " + "(s=%s,d=%s)\n",st->name,s,d); + free(s); free(d); + return; + } +} + +static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds, + const struct timeval *tv_now, uint64_t *now) +{ + struct userv *st=sst; + uint8_t rxbuf[DEFAULT_BUFSIZE]; + int l,i; + + if (fds[1].revents&POLLERR) { + printf("userv_afterpoll: hup!\n"); + } + if (fds[1].revents&POLLIN) { + l=read(st->rxfd,rxbuf,DEFAULT_BUFSIZE); + if (l<0) { + fatal_perror("userv_afterpoll: read(rxfd)"); + } + if (l==0) { + fatal("userv_afterpoll: read(rxfd)=0; userv gone away?\n"); + } + /* XXX really crude unstuff code */ + /* XXX check for buffer overflow */ + for (i=0; ipending_esc) { + st->pending_esc=False; + switch(rxbuf[i]) { + case SLIP_ESCEND: + *(uint8_t *)buf_append(st->buff,1)=SLIP_END; + break; + case SLIP_ESCESC: + *(uint8_t *)buf_append(st->buff,1)=SLIP_ESC; + break; + default: + fatal("userv_afterpoll: bad SLIP escape character\n"); + } + } else { + switch (rxbuf[i]) { + case SLIP_END: + if (st->buff->size>0) process_local_packet(st); + BUF_ASSERT_USED(st->buff); + buffer_init(st->buff,st->max_start_pad); + break; + case SLIP_ESC: + st->pending_esc=True; + break; + default: + *(uint8_t *)buf_append(st->buff,1)=rxbuf[i]; + break; + } + } + } + } + return; +} + +static void userv_phase_hook(void *sst, uint32_t newphase) +{ + struct userv *st=sst; + pid_t child; + int c_stdin[2]; + int c_stdout[2]; + string_t addrs; + string_t nets; + string_t s; + struct netlink_client *c; + int i; + + /* This is where we actually invoke userv - all the networks we'll + 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->local_address), + ipaddr_to_string(st->secnet_address),st->mtu); + + nets=safe_malloc(1024,"userv_phase_hook:nets"); + *nets=0; + for (c=st->clients; c; c=c->next) { + for (i=0; inetworks->entries; i++) { + s=subnet_to_string(&c->networks->list[i]); + strcat(nets,s); + strcat(nets,","); + free(s); + } + } + nets[strlen(nets)-1]=0; + + Message(M_INFO,"\nuserv_phase_hook: %s %s %s %s %s\n",st->userv_path, + st->service_user,st->service_name,addrs,nets); + + /* Allocate buffer, plus space for padding. Make sure we end up + with the start of the packet well-aligned. */ + /* ALIGN(st->max_start_pad,16); */ + /* ALIGN(st->max_end_pad,16); */ + + st->pending_esc=False; + + /* Invoke userv */ + if (pipe(c_stdin)!=0) { + fatal_perror("userv_phase_hook: pipe(c_stdin)"); + } + if (pipe(c_stdout)!=0) { + fatal_perror("userv_phase_hook: pipe(c_stdout)"); + } + st->txfd=c_stdin[1]; + st->rxfd=c_stdout[0]; + + child=fork(); + if (child==-1) { + fatal_perror("userv_phase_hook: fork()"); + } + if (child==0) { + char **argv; + + /* We are the child. Modify our stdin and stdout, then exec userv */ + dup2(c_stdin[0],0); + dup2(c_stdout[1],1); + close(c_stdin[1]); + close(c_stdout[0]); + + /* The arguments are: + userv + service-user + service-name + local-addr,secnet-addr,mtu,protocol + route1,route2,... */ + argv=malloc(sizeof(*argv)*6); + argv[0]=st->userv_path; + argv[1]=st->service_user; + argv[2]=st->service_name; + argv[3]=addrs; + argv[4]=nets; + argv[5]=NULL; + execvp(st->userv_path,argv); + perror("netlink-userv-ipif: execvp"); + + exit(1); + } + /* We are the parent... */ + + /* Register for poll() */ + register_for_poll(st, userv_beforepoll, userv_afterpoll, 2, "netlink"); +} + +static void *userv_regnets(void *sst, struct subnet_list *nets, + netlink_deliver_fn *deliver, void *dst, + uint32_t max_start_pad, uint32_t max_end_pad) +{ + struct userv *st=sst; + struct netlink_client *c; + + Message(M_DEBUG_CONFIG,"userv_regnets: request for %d networks, " + "max_start_pad=%d, max_end_pad=%d\n", + nets->entries,max_start_pad,max_end_pad); + + c=safe_malloc(sizeof(*c),"userv_regnets"); + c->networks=nets; + c->deliver=deliver; + c->dst=dst; + c->next=st->clients; + st->clients=c; + if (max_start_pad > st->max_start_pad) st->max_start_pad=max_start_pad; + if (max_end_pad > st->max_end_pad) st->max_end_pad=max_end_pad; + + return c; +} + +static void userv_deliver(void *sst, void *cid, struct buffer_if *buf) +{ + struct userv *st=sst; + struct netlink_client *client=cid; + uint8_t txbuf[DEFAULT_BUFSIZE]; + + uint32_t source,dest; + uint8_t *i; + uint32_t j; + + source=ntohl(*(uint32_t *)(buf->start+12)); + dest=ntohl(*(uint32_t *)(buf->start+16)); + + /* Check that the packet source is in 'nets' and its destination is + in client->networks */ + if (!subnet_match(client->networks,source)) { + string_t s,d; + s=ipaddr_to_string(source); + d=ipaddr_to_string(dest); + Message(M_WARNING,"%s: incoming packet with bad source address " + "(s=%s,d=%s)\n",st->name,s,d); + free(s); free(d); + return; + } + if (!subnet_match(&st->networks,dest)) { + string_t s,d; + s=ipaddr_to_string(source); + d=ipaddr_to_string(dest); + Message(M_WARNING,"%s: incoming packet with bad destination address " + "(s=%s,d=%s)\n",st->name,s,d); + free(s); free(d); + return; + } + + /* Really we should decrease TTL, check it's above zero, and + recalculate header checksum here. If it gets down to zero, + generate an ICMP time-exceeded and send the new packet back to + the originating tunnel. XXX check buffer usage! */ + + /* (Basically do full IP packet forwarding stuff. Except that we + know any packet passed in here is destined for the local + machine; only exception is if it's destined for us.) */ + + if (dest==st->secnet_address) { + printf("%s: incoming tunneled packet for secnet!\n",st->name); + return; + } + + /* Now spit the packet at userv-ipif: SLIP start marker, then + bytestuff the packet, then SLIP end marker */ + /* XXX crunchy bytestuff code */ + j=0; + txbuf[j++]=SLIP_END; + for (i=buf->start; i<(buf->start+buf->size); i++) { + switch (*i) { + case SLIP_END: + txbuf[j++]=SLIP_ESC; + txbuf[j++]=SLIP_ESCEND; + break; + case SLIP_ESC: + txbuf[j++]=SLIP_ESC; + txbuf[j++]=SLIP_ESCESC; + break; + default: + txbuf[j++]=*i; + break; + } + } + txbuf[j++]=SLIP_END; + if (write(st->txfd,txbuf,j)<0) { + fatal_perror("userv_deliver: write()"); + } + + return; +} + +static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct userv *st; + item_t *item; + dict_t *dict; + + st=safe_malloc(sizeof(*st),"userv_apply (netlink)"); + st->cl.description="userv-netlink"; + st->cl.type=CL_NETLINK; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.regnets=userv_regnets; + st->ops.deliver=userv_deliver; + st->max_start_pad=0; + st->max_end_pad=0; + st->rxfd=-1; st->txfd=-1; + st->clients=NULL; + + /* First parameter must be a dict */ + item=list_elem(args,0); + if (!item || item->type!=t_dict) + cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n"); + + dict=item->data.dict; + st->name=dict_read_string(dict,"name",False,"userv-netlink",loc); + st->userv_path=dict_read_string(dict,"userv-path",False,"userv-netlink", + loc); + st->service_user=dict_read_string(dict,"service-user",False, + "userv-netlink",loc); + st->service_name=dict_read_string(dict,"service-name",False, + "userv-netlink",loc); + if (!st->name) st->name="netlink-userv-ipif"; + if (!st->userv_path) st->userv_path="userv"; + if (!st->service_user) st->service_user="root"; + if (!st->service_name) st->service_name="ipif"; + dict_read_subnet_list(dict, "networks", True, "userv-netlink", loc, + &st->networks); + st->local_address=string_to_ipaddr( + dict_find_item(dict,"local-address", True, "userv-netlink", loc), + "userv-netlink"); + st->secnet_address=string_to_ipaddr( + dict_find_item(dict,"secnet-address", True, "userv-netlink", loc), + "userv-netlink"); + if (!subnet_match(&st->networks,st->local_address)) { + cfgfatal(loc,"netlink-userv-ipif","local-address must be in " + "local networks\n"); + } + st->mtu=dict_read_number(dict, "mtu", False, "userv-netlink", loc, 1000); + st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"userv-netlink",loc); + BUF_ALLOC(st->buff,"netlink:userv_apply"); + + add_hook(PHASE_DROPPRIV,userv_phase_hook,st); + + return new_closure(&st->cl); +} + +struct null { + closure_t cl; + struct netlink_if ops; +}; + +static void *null_regnets(void *sst, struct subnet_list *nets, + netlink_deliver_fn *deliver, void *dst, + uint32_t max_start_pad, uint32_t max_end_pad) +{ + Message(M_DEBUG_CONFIG,"null_regnets: request for %d networks, " + "max_start_pad=%d, max_end_pad=%d\n", + nets->entries,max_start_pad,max_end_pad); + return NULL; +} + +static void null_deliver(void *sst, void *cid, struct buffer_if *buf) +{ + return; +} + +static list_t *null_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct null *st; + + st=safe_malloc(sizeof(*st),"null_apply (netlink)"); + st->cl.description="null-netlink"; + st->cl.type=CL_NETLINK; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.regnets=null_regnets; + st->ops.deliver=null_deliver; + + return new_closure(&st->cl); +} + +init_module netlink_module; +void netlink_module(dict_t *dict) +{ + add_closure(dict,"userv-ipif",userv_apply); +#if 0 + add_closure(dict,"pty-slip",ptyslip_apply); + add_closure(dict,"slipd",slipd_apply); +#endif /* 0 */ + add_closure(dict,"null-netlink",null_apply); +} diff --git a/private-key b/private-key new file mode 100644 index 0000000000000000000000000000000000000000..f764068413fa9fd407e7107a6ecc19ef8ec1e64e GIT binary patch literal 528 zcmV+r0`L7(Q%E3CQb|@pR7D_5MOh$5NlZl`Mo&^rK~x|yE-?xK000000000400aQr zbhZni$fos+%!yXo=)#qB!b8TNh=USvj~h{7x&)z~`nDyXb>;V4u0tV2tiJP8$s%Q- zvK8!n(B>8d`rb3Xk`M?zVtIC#rsy)!m_EKnE*?UL=OQ5Mk4tK*W^~Y@By!NlxsQ9q zlgxm-fnE8_pry6_dUYdLQw15|3(aQ$1|t9f01b0=Wp-siZFzEJWNB{W2;&F?{Swc8 zQFJbJwe|`(s9Wd|t+|Z;e!VxAG{@1vgHdk>wiNK|3W@^Ck&A=(ARE+5;iwpm>vSxI ziA;v?CUta{v(dr#1$>C>gQ*U{u~Nmtk5EAzZ}m@^pdVB1Cx2Oqk;Tne?nn{o>`<(@ zm+VB`xM+1+D*^o(EYD8LxE#d3pn0`sc$2Dg1}pDoz4)5tEc&i$ z66z8k73e2MuO@dLfAOKG=VeZ8${a|a(Ht?Y$h^m8JGFoU0P7~8?P-Eo{+c{QQT?6; zhy~A@0rM=}n0d~6|HK@%4k{4TanAhIAxjz7TOz?SqirgCU}~K2iGaPh@W?+1*8%|U z>DSuPqPilUX)z&Vp|Gw(yf}Ni8Jdx +#include +#include +#include +#include +#include + +#include "secnet.h" + +struct rgen_data { + closure_t cl; + struct random_if ops; + struct cloc loc; + int fd; +}; + +static random_fn random_generate; +static bool_t random_generate(void *data, uint32_t bytes, uint8_t *buff) +{ + struct rgen_data *st=data; + + /* XXX XXX error checking */ + read(st->fd,buff,bytes); + + return True; +} + +static list_t *random_apply(closure_t *self, struct cloc loc, + dict_t *context, list_t *args) +{ + struct rgen_data *st; + item_t *arg1, *arg2; + string_t filename=NULL; + + st=safe_malloc(sizeof(*st),"random_apply"); + + st->cl.description="randomsource"; + st->cl.type=CL_RANDOMSRC; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.blocking=False; + st->ops.generate=random_generate; + st->loc=loc; + + arg1=list_elem(args,0); + arg2=list_elem(args,1); + + if (!arg1) { + fatal("randomsource: requires a filename\n"); + } + if (arg1->type != t_string) { + cfgfatal(arg1->loc,"randomsource", + "filename (arg1) must be a string\n"); + } + filename=arg1->data.string; + + if (arg2) { + if (arg2->type != t_bool) { + cfgfatal(arg2->loc,"randomsource", + "blocking parameter (arg2) must be bool\n"); + } + st->ops.blocking=arg2->data.bool; + } + + if (!filename) { + fatal("randomsource requires a filename"); + } + st->fd=open(filename,O_RDONLY); + if (st->fd<0) { + fatal_perror("randomsource (%s:%d): cannot open %s",arg1->loc.file, + arg1->loc.line,filename); + } + return new_closure(&st->cl); +} + +void random_module(dict_t *d) +{ + add_closure(d,"randomfile",random_apply); +} diff --git a/resolver.c b/resolver.c new file mode 100644 index 0000000..9e0530c --- /dev/null +++ b/resolver.c @@ -0,0 +1,125 @@ +/* Name resolution using adns */ + +#include +#include "secnet.h" +#include + +struct adns { + closure_t cl; + struct resolver_if ops; + struct cloc loc; + adns_state ast; +}; + +struct query { + void *cst; + resolve_answer_fn *answer; + adns_query query; +}; + +static bool_t resolve_request(void *sst, string_t name, + resolve_answer_fn *cb, void *cst) +{ + struct adns *st=sst; + struct query *q; + int rv; + + q=safe_malloc(sizeof *q,"resolve_request"); + q->cst=cst; + q->answer=cb; + + rv=adns_submit(st->ast, name, adns_r_a, 0, q, &q->query); + + return rv==0; +} + +static int resolver_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, + int *timeout_io, const struct timeval *tv_now, + uint64_t *now) +{ + struct adns *st=sst; + return adns_beforepoll(st->ast, fds, nfds_io, timeout_io, tv_now); +} + +static void resolver_afterpoll(void *sst, struct pollfd *fds, int nfds, + const struct timeval *tv_now, uint64_t *now) +{ + struct adns *st=sst; + adns_query aq; + adns_answer *ans; + void *qp; + struct query *q; + int rv; + + adns_afterpoll(st->ast, fds, nfds, tv_now); + + while (True) { + aq=NULL; + rv=adns_check(st->ast, &aq, &ans, &qp); + if (rv==0) { + q=qp; + if (ans->status!=adns_s_ok) { + q->answer(q->cst,NULL); /* Failure */ + free(q); + free(ans); + } else { + q->answer(q->cst,ans->rrs.inaddr); + free(q); + free(ans); + } + } else if (rv==EAGAIN || rv==ESRCH) { + break; + } else { + fatal("resolver_afterpoll: adns_check() returned %d\n",rv); + } + } + + return; +} + +/* Initialise adns, using parameters supplied */ +static list_t *adnsresolver_apply(closure_t *self, struct cloc loc, + dict_t *context, list_t *args) +{ + struct adns *st; + dict_t *d; + item_t *i; + string_t conf; + + st=safe_malloc(sizeof(*st),"adnsresolver_apply"); + st->cl.description="adns"; + st->cl.type=CL_RESOLVER; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->loc=loc; + st->ops.st=st; + st->ops.request=resolve_request; + + i=list_elem(args,0); + if (!i || i->type!=t_dict) { + cfgfatal(st->loc,"adns","first argument must be a dictionary\n"); + } + d=i->data.dict; + conf=dict_read_string(d,"config",False,"adns",loc); + + if (conf) { + if (adns_init_strcfg(&st->ast, 0, 0, conf)) { + fatal_perror("Failed to initialise ADNS"); + } + } else { + if (adns_init(&st->ast, 0, 0)) { + fatal_perror("Failed to initialise ADNS"); + } + } + + register_for_poll(st, resolver_beforepoll, resolver_afterpoll, + ADNS_POLLFDS_RECOMMENDED+5,"resolver"); + + return new_closure(&st->cl); +} + +init_module resolver_module; +void resolver_module(dict_t *dict) +{ + add_closure(dict,"adns",adnsresolver_apply); +} diff --git a/rsa.c b/rsa.c new file mode 100644 index 0000000..d1107a9 --- /dev/null +++ b/rsa.c @@ -0,0 +1,338 @@ +/*************************************************************************** + * + * Part II Project, "A secure, private IP network" + * Stephen Early + * + * + * $RCSfile: rsa.c,v $ + * + * Description: RSA signature making and checking functions + * + * Copyright: (C) Stephen Early 1995 + * + * $Revision: 1.1 $ + * + * $Date: 1996/05/16 18:40:14 $ + * + * $State: Exp $ + * + ***************************************************************************/ + +/* $Log: rsa.c,v $ + * Revision 1.1 1996/05/16 18:40:14 sde1000 + * Initial revision + * + */ + +#include +#include +#include "secnet.h" +#include "util.h" + +#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" + +struct rsapriv { + closure_t cl; + struct rsaprivkey_if ops; + struct cloc loc; + MP_INT d; + MP_INT n; +}; +struct rsapub { + closure_t cl; + struct rsapubkey_if ops; + struct cloc loc; + MP_INT e; + MP_INT n; +}; +/* Sign data. NB data must be smaller than modulus */ + +static char *hexchars="0123456789abcdef"; + +static string_t rsa_sign(void *sst, uint8_t *data, uint32_t datalen) +{ + struct rsapriv *st=sst; + MP_INT a, b; + char buff[2048]; + int msize, i; + string_t signature; + + mpz_init(&a); + mpz_init(&b); + + msize=mpz_sizeinbase(&st->n, 16); + + strcpy(buff,"0001"); + + for (i=0; i>4]; + buff[5+i*2]=hexchars[data[i]&0xf]; + } + buff[4+datalen*2]=0; + + for (i=datalen*2+4; id, &st->n); + + signature=write_mpstring(&b); + + mpz_clear(&b); + mpz_clear(&a); + return signature; +} + +static bool_t rsa_sig_check(void *sst, uint8_t *data, uint32_t datalen, + string_t signature) +{ + struct rsapub *st=sst; + MP_INT a, b, c; + char buff[2048]; + int msize, i; + bool_t ok; + + mpz_init(&a); + mpz_init(&b); + mpz_init(&c); + + msize=mpz_sizeinbase(&st->n, 16); + + strcpy(buff,"0001"); + + for (i=0; i>4]; + buff[5+i*2]=hexchars[data[i]&0xf]; + } + buff[4+datalen*2]=0; + + for (i=datalen*2+4; ie, &st->n); + + ok=(mpz_cmp(&a, &c)==0); + + mpz_clear(&c); + mpz_clear(&b); + mpz_clear(&a); + + return ok; +} + +static list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct rsapub *st; + item_t *i; + string_t e,n; + + st=safe_malloc(sizeof(*st),"rsapub_apply"); + st->cl.description="rsapub"; + st->cl.type=CL_RSAPUBKEY; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.check=rsa_sig_check; + st->loc=loc; + + i=list_elem(args,0); + if (i) { + if (i->type!=t_string) { + cfgfatal(i->loc,"rsa-public","first argument must be a string"); + } + e=i->data.string; + if (mpz_init_set_str(&st->e,e,10)!=0) { + cfgfatal(i->loc,"rsa-public","encryption key \"%s\" is not a " + "decimal number string\n",e); + } + } else { + cfgfatal(loc,"rsa-public","you must provide an encryption key\n"); + } + + i=list_elem(args,1); + if (i) { + if (i->type!=t_string) { + cfgfatal(i->loc,"rsa-public","second argument must be a string"); + } + n=i->data.string; + if (mpz_init_set_str(&st->n,n,10)!=0) { + cfgfatal(i->loc,"rsa-public","modulus \"%s\" is not a decimal " + "number string\n",n); + } + } else { + cfgfatal(loc,"rsa-public","you must provide a modulus\n"); + } + return new_closure(&st->cl); +} + +static uint32_t keyfile_get_int(FILE *f) +{ + uint32_t r; + r=fgetc(f)<<24; + r|=fgetc(f)<<16; + r|=fgetc(f)<<8; + r|=fgetc(f); + return r; +} + +static uint16_t keyfile_get_short(FILE *f) +{ + uint16_t r; + r=fgetc(f)<<8; + r|=fgetc(f); + return r; +} + +static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct rsapriv *st; + FILE *f; + string_t filename; + item_t *i; + long length; + uint8_t *b, *c; + int cipher_type; + MP_INT e,sig,plain,check; + + st=safe_malloc(sizeof(*st),"rsapriv_apply"); + st->cl.description="rsapriv"; + st->cl.type=CL_RSAPRIVKEY; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.sign=rsa_sign; + st->loc=loc; + + /* Argument is filename pointing to SSH1 private key file */ + i=list_elem(args,0); + if (i) { + if (i->type!=t_string) { + cfgfatal(i->loc,"rsa-public","first argument must be a string"); + } + filename=i->data.string; + } else { + filename=""; /* Make compiler happy */ + cfgfatal(loc,"rsa-private","you must provide a filename\n"); + } + + f=fopen(filename,"rb"); + if (!f) { + fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"", + loc.file,loc.line,filename); + } + + /* Check that the ID string is correct */ + length=strlen(AUTHFILE_ID_STRING)+1; + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1 || memcmp(b,AUTHFILE_ID_STRING,length)!=0) { + cfgfatal(loc,"rsa-private","file \"%s\" is not a " + "SSH1 private keyfile\n",filename); + } + free(b); + + cipher_type=fgetc(f); + keyfile_get_int(f); /* "Reserved data" */ + if (cipher_type != 0) { + cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n"); + } + + /* Read the public key */ + keyfile_get_int(f); /* Not sure what this is */ + length=(keyfile_get_short(f)+7)/8; + if (length>1024) { + cfgfatal(loc,"rsa-private","implausible length %ld for modulus\n", + length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f) != 1) { + cfgfatal(loc,"rsa-private","error reading modulus\n"); + } + mpz_init(&st->n); + read_mpbin(&st->n,b,length); + free(b); + length=(keyfile_get_short(f)+7)/8; + if (length>1024) { + cfgfatal(loc,"rsa-private","implausible length %ld for e\n",length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1) { + cfgfatal(loc,"rsa-private","error reading e\n"); + } + mpz_init(&e); + read_mpbin(&e,b,length); + free(b); + + length=keyfile_get_int(f); + if (length>1024) { + cfgfatal(loc,"rsa-private","implausibly long (%ld) key comment\n", + length); + } + c=safe_malloc(length+1,"rsapriv_apply"); + if (fread(c,length,1,f)!=1) { + cfgfatal(loc,"rsa-private","error reading key comment\n"); + } + c[length]=0; + + /* Check that the next two pairs of characters are identical - the + keyfile is not encrypted, so they should be */ + if (keyfile_get_short(f) != keyfile_get_short(f)) { + cfgfatal(loc,"rsa-private","corrupt keyfile\n"); + } + + /* Read d */ + length=(keyfile_get_short(f)+7)/8; + if (length>1024) { + cfgfatal(loc,"rsa-private","implausibly long (%ld) decryption key\n", + length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1) { + cfgfatal(loc,"rsa-private","error reading decryption key\n"); + } + mpz_init(&st->d); + read_mpbin(&st->d,b,length); + free(b); + + if (fclose(f)!=0) { + fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line); + } + + /* Now do trial signature/check to make sure it's a real keypair: + sign the comment string! */ + mpz_init(&sig); + mpz_init(&plain); + mpz_init(&check); + read_mpbin(&plain,c,strlen(c)); + mpz_powm(&sig, &plain, &st->d, &st->n); + mpz_powm(&check, &sig, &e, &st->n); + if (mpz_cmp(&plain,&check)!=0) { + cfgfatal(loc,"rsa-private","file \"%s\" does not contain a " + "valid RSA key!\n",filename); + } + mpz_clear(&sig); + mpz_clear(&plain); + mpz_clear(&check); + + free(c); + mpz_clear(&e); + + return new_closure(&st->cl); +} + +init_module rsa_module; +void rsa_module(dict_t *dict) +{ + add_closure(dict,"rsa-private",rsapriv_apply); + add_closure(dict,"rsa-public",rsapub_apply); +} diff --git a/secnet.c b/secnet.c new file mode 100644 index 0000000..0df6e09 --- /dev/null +++ b/secnet.c @@ -0,0 +1,359 @@ +/* $Log: secnet.c,v $ + * Revision 1.1 1996/03/13 22:27:41 sde1000 + * Initial revision + * + */ + +static char *version="secnet version " VERSION " $Date: 1996/03/13 22:27:41 $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "secnet.h" +#include "util.h" +#include "conffile.h" +#include "modules.h" + +/* Command-line options (possibly config-file options too) */ +static char *configfile="/etc/secnet/secnet.conf"; +static char *userid=NULL; +static uid_t uid=0; +static bool_t background=True; +static char *pidfile=NULL; + +/* Structures dealing with poll() call */ +struct poll_interest { + beforepoll_fn *before; + afterpoll_fn *after; + void *state; + uint32_t max_nfds; + uint32_t nfds; + string_t desc; + struct poll_interest *next; +}; +static struct poll_interest *reg=NULL; +static uint32_t total_nfds=10; + +static bool_t finished=False; + +/* Parse the command line options */ +static void parse_options(int argc, char **argv) +{ + int c; + + while (True) { + int option_index = 0; + static struct option long_options[] = { + {"verbose", 0, 0, 'v'}, + {"nowarnings", 0, 0, 'w'}, + {"help", 0, 0, 2}, + {"version", 0, 0, 1}, + {"nodetach", 0, 0, 'n'}, + {"silent", 0, 0, 'f'}, + {"quiet", 0, 0, 'f'}, + {"debug", 1, 0, 'd'}, + {"config", 1, 0, 'c'}, + {0,0,0,0} + }; + + c=getopt_long(argc, argv, "vwdnc:ft:", + long_options, &option_index); + if (c==-1) + break; + + switch(c) { + case 2: + /* Help */ + fprintf(stderr, + "Usage: secnet [OPTION]...\n\n" + " -f, --silent, --quiet suppress error messages\n" + " -w, --nowarnings suppress warnings\n" + " -v, --verbose output extra diagnostics\n" + " -c, --config=filename specify a configuration file\n" + " -n, --nodetach do not run in background\n" + " -d, --debug=item,... set debug options\n" + " --help display this help and exit\n" + " --version output version information and exit\n" + ); + exit(0); + break; + + case 1: + /* Version */ + fprintf(stderr,"%s\n",version); + exit(0); + break; + + case 'v': + message_level|=M_INFO|M_WARNING|M_ERROR|M_FATAL; + break; + + case 'n': + background=False; + break; + + case 'd': + message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE; + break; + + case 'f': + message_level=M_FATAL; + break; + + case 'c': + if (optarg) + configfile=safe_strdup(optarg,"config_filename"); + else + fatal("secnet: no config filename specified"); + break; + + case '?': + break; + + default: + Message(M_WARNING,"secnet: Unknown getopt code %c\n",c); + } + } + + if (argc-optind != 0) { + Message(M_WARNING,"secnet: You gave extra command line parameters, " + "which were ignored.\n"); + } +} + +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; + + l=dict_lookup(config,"system"); + + if (!l || list_elem(l,0)->type!=t_dict) { + fatal("configuration does not include a \"system\" dictionary\n"); + } + system=list_elem(l,0)->data.dict; + loc=list_elem(l,0)->loc; + + /* Arrange systemwide log facility */ + l=dict_lookup(system,"log"); + if (!l) { + fatal("configuration does not include a system/log facility\n"); + } + log=init_log(l); + log->log(log->st,LOG_DEBUG,"setup: logging started"); + + /* Who are we supposed to run as? */ + userid=dict_read_string(system,"userid",False,"system",loc); + if (userid) { + do { + pw=getpwent(); + if (pw && strcmp(pw->pw_name,userid)==0) { + uid=pw->pw_uid; + break; + } + } while(pw); + endpwent(); + if (uid==0) { + fatal("userid \"%s\" not found\n",userid); + } + } + + /* Pidfile name */ + pidfile=dict_read_string(system,"pidfile",False,"system",loc); + + /* Go along site list, starting sites */ + l=dict_lookup(config,"sites"); + if (!l) { + fatal("configuration did not define any remote sites\n"); + } + i=0; + while ((site=list_elem(l, i++))) { + struct site_if *s; + if (site->type!=t_closure) { + cfgfatal(site->loc,"system","non-closure in site list"); + } + if (site->data.closure->type!=CL_SITE) { + cfgfatal(site->loc,"system","non-site closure in site list"); + } + s=site->data.closure->interface; + s->control(s->st,True); + } +} + +void register_for_poll(void *st, beforepoll_fn *before, + afterpoll_fn *after, uint32_t max_nfds, string_t desc) +{ + struct poll_interest *i; + + i=safe_malloc(sizeof(*i),"register_for_poll"); + i->before=before; + i->after=after; + i->state=st; + i->max_nfds=max_nfds; + i->nfds=0; + i->desc=desc; + total_nfds+=max_nfds; + i->next=reg; + reg=i; + return; +} + +static void system_phase_hook(void *sst, uint32_t newphase) +{ + if (newphase==PHASE_SHUTDOWN && pidfile) { + /* Try to unlink the pidfile; don't care if it fails */ + unlink(pidfile); + } +} + +static void run(void) +{ + struct timeval tv_now; + uint64_t now; + struct poll_interest *i; + int rv, nfds, remain, idx; + int timeout; + struct pollfd *fds; + + fds=alloca(sizeof(*fds)*total_nfds); + if (!fds) { + fatal("run: couldn't alloca\n"); + } + + while (!finished) { + if (gettimeofday(&tv_now, NULL)!=0) { + fatal_perror("main loop: gettimeofday"); + } + now=(tv_now.tv_sec*1000)+(tv_now.tv_usec/1000); + idx=0; + for (i=reg; i; i=i->next) { + i->after(i->state, fds+idx, i->nfds, &tv_now, &now); + idx+=i->nfds; + } + remain=total_nfds; + idx=0; + timeout=-1; + for (i=reg; i; i=i->next) { + nfds=remain; + rv=i->before(i->state, fds+idx, &nfds, &timeout, &tv_now, &now); + if (rv!=0) { + /* XXX we need to handle this properly: increase the + nfds available */ + fatal("run: beforepoll_fn (%s) returns %d\n",i->desc,rv); + } + if (timeout<-1) { + fatal("run: beforepoll_fn (%s) set timeout to %d\n",timeout); + } + idx+=nfds; + remain-=nfds; + i->nfds=nfds; + } + do { + rv=poll(fds, idx, timeout); + if (rv<0) { + if (errno!=EINTR) { + fatal_perror("run: poll"); + } + } + } while (rv<0); + } +} + +static void droppriv(void) +{ + FILE *pf=NULL; + pid_t p; + + 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) { + printf("goto background\n"); + /* 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"); + } + p=fork(); + if (p>0) { + if (pf) { + /* Parent process - write pidfile, exit */ + fprintf(pf,"%d\n",p); + fclose(pf); + } + exit(0); + } else if (p==0) { + /* Child process - all done, just carry on */ + if (pf) fclose(pf); + printf("child\n"); + } 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) +{ + dict_t *config; + + enter_phase(PHASE_GETOPTS); + parse_options(argc,argv); + + enter_phase(PHASE_READCONFIG); + config=read_conffile(configfile); + + enter_phase(PHASE_SETUP); + setup(config); + + enter_phase(PHASE_DROPPRIV); + droppriv(); + + enter_phase(PHASE_RUN); + run(); + + enter_phase(PHASE_SHUTDOWN); + + return 0; +} + diff --git a/secnet.h b/secnet.h new file mode 100644 index 0000000..ce5f9d6 --- /dev/null +++ b/secnet.h @@ -0,0 +1,401 @@ +/* Core interface of secnet, to be used by all modules */ + +#ifndef secnet_h +#define secnet_h + +#include +#include +#include +#include +#include +#include +#include "config.h" + + +typedef char *string_t; +typedef enum {False,True} bool_t; + +#define ASSERT(x) do { if (!(x)) { fatal("assertion failed line " __LINE__ \ + " file " __FILE__ "\n"); } while(0) + +/***** SHARED types *****/ + +/* These are stored in HOST byte order */ +struct subnet { + uint32_t prefix; + uint32_t mask; +}; + +struct subnet_list { + uint32_t entries; + 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_list *list, uint32_t address); + +/***** END of shared types *****/ + +/***** CONFIGURATION support *****/ + +typedef struct dict dict_t; /* Configuration dictionary */ +typedef struct closure closure_t; +typedef struct item item_t; +typedef struct list list_t; /* A list of items */ + +/* Configuration file location, for error-reporting */ +struct cloc { + string_t file; + uint32_t line; +}; + +/* Modules export closures, which can be invoked from the configuration file. + "Invoking" a closure usually returns another closure (of a different + type), but can actually return any configuration object. */ +typedef list_t *(apply_fn)(closure_t *self, struct cloc loc, + dict_t *context, list_t *data); +struct closure { + string_t description; /* For debugging */ + uint32_t type; /* Central registry... */ + apply_fn *apply; + void *interface; /* Interface for use inside secnet; depends on type */ +}; + +enum types { t_null, t_bool, t_string, t_number, t_dict, t_closure }; +struct item { + enum types type; + union { + bool_t bool; + string_t string; + uint32_t number; + dict_t *dict; + closure_t *closure; + } data; + struct cloc loc; +}; + +struct list { + item_t *item; + struct list *next; +}; + +/* In the following two lookup functions, NULL means 'not found' */ +/* Lookup a value in the specified dictionary, or its parents */ +extern list_t *dict_lookup(dict_t *dict, string_t key); +/* Lookup a value in just the specified dictionary */ +extern list_t *dict_lookup_primitive(dict_t *dict, string_t key); +/* Add a value to the specified dictionary */ +extern void dict_add(dict_t *dict, string_t key, list_t *val); +/* Obtain an array of keys in the dictionary. malloced; caller frees */ +extern string_t *dict_keys(dict_t *dict); + +/* List-manipulation functions */ +extern list_t *list_new(void); +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 */ +extern item_t *list_elem(list_t *l, uint32_t index); + +/* Convenience functions */ +extern list_t *new_closure(closure_t *cl); +extern void add_closure(dict_t *dict, string_t name, apply_fn apply); +extern void *find_cl_if(dict_t *dict, string_t name, uint32_t type, + bool_t fail_if_invalid, string_t desc, + struct cloc loc); +extern item_t *dict_find_item(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc); +extern string_t dict_read_string(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc); +extern uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc, uint32_t def); +extern bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc, bool_t def); +extern void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required, + string_t desc, struct cloc loc, + struct subnet_list *sl); +extern uint32_t string_to_ipaddr(item_t *i, string_t desc); + +/***** END of configuration support *****/ + +/***** 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 + +extern void fatal(char *message, ...); +extern void fatal_perror(char *message, ...); +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 utility functions *****/ + +/***** SCHEDULING support */ + +/* "now" is current program time, in milliseconds. It is derived + (once) from tv_now. If nfds_io is insufficient for your needs, set + it to the required number and return ERANGE. timeout is in milliseconds; + if it is too high then lower it. It starts at -1 (==infinite) */ +typedef int beforepoll_fn(void *st, struct pollfd *fds, int *nfds_io, + int *timeout_io, const struct timeval *tv_now, + uint64_t *now); +typedef void afterpoll_fn(void *st, struct pollfd *fds, int nfds, + const struct timeval *tv_now, uint64_t *now); + +/* Register interest in the main loop of the program. Before a call + to poll() your supplied beforepoll function will be called. After + the call to poll() the supplied afterpoll function will be called. + max_nfds is a _hint_ about the maximum number of struct pollfd + structures you may require - you can always ask for more in + *nfds_io. */ +extern void register_for_poll(void *st, beforepoll_fn *before, + afterpoll_fn *after, uint32_t max_nfds, + string_t desc); + +/***** END of scheduling support */ + +/***** PROGRAM LIFETIME support */ + +/* The secnet program goes through a number of phases in its lifetime. + Module code may arrange to be called just as various phases are + entered. */ + +#define PHASE_INIT 0 +#define PHASE_GETOPTS 1 /* Process command-line arguments */ +#define PHASE_READCONFIG 2 /* Parse and process configuration file */ +#define PHASE_SETUP 3 /* Process information in configuration */ +#define PHASE_DROPPRIV 4 /* Last chance for privileged operations */ +#define PHASE_RUN 5 +#define PHASE_SHUTDOWN 6 /* About to die; delete key material, etc. */ +#define NR_PHASES 7 + +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); + +/***** END of program lifetime support *****/ + +/***** MODULE support *****/ + +/* Module initialisation function type - modules export one function of + this type which is called to initialise them. For dynamically loaded + modules it's called "secnet_module". */ +typedef void (init_module)(dict_t *dict); + +/***** END of module support *****/ + +/***** CLOSURE TYPES and interface definitions *****/ + +#define CL_PURE 0 +#define CL_RESOLVER 1 +#define CL_RANDOMSRC 2 +#define CL_RSAPUBKEY 3 +#define CL_RSAPRIVKEY 4 +#define CL_COMM 5 +#define CL_IPIF 6 +#define CL_LOG 7 +#define CL_SITE 8 +#define CL_TRANSFORM 9 +#define CL_NETLINK 10 +#define CL_DH 11 +#define CL_HASH 12 +#define CL_BUFFER 13 + +struct buffer_if; + +/* PURE closure requires no interface */ + +/* RESOLVER interface */ + +/* Answers to queries are delivered to a function of this + type. 'address' will be NULL if there was a problem with the query. It + will be freed once resolve_answer_fn returns. It is in network byte + order. */ +typedef void resolve_answer_fn(void *st, struct in_addr *addr); +typedef bool_t resolve_request_fn(void *st, string_t name, + resolve_answer_fn *cb, void *cst); +struct resolver_if { + void *st; + resolve_request_fn *request; +}; + +/* RANDOMSRC interface */ + +/* Return some random data. Returns TRUE for success. */ +typedef bool_t random_fn(void *st, uint32_t bytes, uint8_t *buff); + +struct random_if { + void *st; + bool_t blocking; + random_fn *generate; +}; + +/* RSAPUBKEY interface */ + +typedef bool_t rsa_checksig_fn(void *st, uint8_t *data, uint32_t datalen, + string_t signature); +struct rsapubkey_if { + void *st; + rsa_checksig_fn *check; +}; + +/* RSAPRIVKEY interface */ + +typedef string_t rsa_makesig_fn(void *st, uint8_t *data, uint32_t datalen); +struct rsaprivkey_if { + void *st; + rsa_makesig_fn *sign; +}; + +/* COMM interface */ + +/* Return True if the packet was processed, and shouldn't be passed to + any other potential receivers. */ +typedef bool_t comm_notify_fn(void *state, struct buffer_if *buf, + struct sockaddr_in *source); +typedef void comm_request_notify_fn(void *commst, void *nst, + comm_notify_fn *fn); +typedef void comm_release_notify_fn(void *commst, void *nst, + comm_notify_fn *fn); +typedef bool_t comm_sendmsg_fn(void *commst, struct buffer_if *buf, + struct sockaddr_in *dest); +struct comm_if { + void *st; + comm_request_notify_fn *request_notify; + comm_release_notify_fn *release_notify; + comm_sendmsg_fn *sendmsg; +}; + +/* 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); +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, ...); + +/* SITE interface */ + +/* Pretty much a placeholder; allows starting and stopping of processing, + key expiry, etc. */ +typedef void site_control_fn(void *st, bool_t run); +typedef uint32_t site_status_fn(void *st); +struct site_if { + void *st; + site_control_fn *control; + site_status_fn *status; +}; + +/* TRANSFORM interface */ + +/* A reversable transformation. Transforms buffer in-place; may add + data to start or end. Maximum amount of data to be added specified + in max_start_pad and max_end_pad. (Reverse transformations decrease + length, of course.) Transformations may be key-dependent, in which + case key material is passed in at initialisation time. They may + also depend on internal factors (eg. time) and keep internal + state. A struct transform_if only represents a particular type of + transformation; instances of the transformation (eg. with + particular key material) have a different C type. */ + +typedef struct transform_inst_if *transform_createinstance_fn(void *st); +typedef bool_t transform_setkey_fn(void *st, uint8_t *key, uint32_t keylen); +typedef void transform_delkey_fn(void *st); +typedef void transform_destroyinstance_fn(void *st); +/* Returns 0 for 'all is well', any other value for a problem */ +typedef uint32_t transform_apply_fn(void *st, struct buffer_if *buf, + char **errmsg); + +struct transform_inst_if { + void *st; + transform_setkey_fn *setkey; + transform_delkey_fn *delkey; + transform_apply_fn *forwards; + transform_apply_fn *reverse; + transform_destroyinstance_fn *destroy; +}; + +struct transform_if { + void *st; + uint32_t max_start_pad; + uint32_t max_end_pad; + uint32_t keylen; + transform_createinstance_fn *create; +}; + +/* NETLINK interface */ + +/* Used by netlink to deliver to site, and by site to deliver to netlink. + cid is the client identifier returned by netlink_regnets_fn */ +typedef void netlink_deliver_fn(void *st, void *cid, struct buffer_if *buf); +/* Register for packets from specified networks. Return value is client + identifier. */ +typedef void *netlink_regnets_fn(void *st, struct subnet_list *networks, + netlink_deliver_fn *deliver, void *dst, + uint32_t max_start_pad, uint32_t max_end_pad); + +struct netlink_if { + void *st; + netlink_regnets_fn *regnets; + netlink_deliver_fn *deliver; +}; + +/* DH interface */ + +/* Returns public key as a malloced hex string */ +typedef string_t dh_makepublic_fn(void *st, uint8_t *secret, + uint32_t secretlen); +/* Fills buffer (up to buflen) with shared secret */ +typedef void dh_makeshared_fn(void *st, uint8_t *secret, + uint32_t secretlen, string_t rempublic, + uint8_t *sharedsecret, uint32_t buflen); +struct dh_if { + void *st; + uint32_t len; /* Approximate size of modulus in bytes */ + dh_makepublic_fn *makepublic; + dh_makeshared_fn *makeshared; +}; + +/* HASH interface */ + +typedef void *hash_init_fn(void); +typedef void hash_update_fn(void *st, uint8_t const *buf, uint32_t len); +typedef void hash_final_fn(void *st, uint8_t *digest); +struct hash_if { + uint32_t len; /* Hash output length in bytes */ + hash_init_fn *init; + hash_update_fn *update; + hash_final_fn *final; +}; + +/* BUFFER interface */ + +struct buffer_if { + bool_t free; + string_t owner; /* Set to constant string */ + uint32_t flags; /* How paranoid should we be? */ + struct cloc loc; /* Where we were defined */ + uint8_t *base; + uint8_t *start; + uint32_t size; /* Size of buffer contents */ + uint32_t len; /* Total length allocated at base */ +}; + +#endif /* secnet_h */ diff --git a/serpent.c b/serpent.c new file mode 100644 index 0000000..51b27ba --- /dev/null +++ b/serpent.c @@ -0,0 +1,320 @@ +/* + * This file is + * Copyright (C) 1998 Ross Anderson, Eli Biham, Lars Knudsen + * + * For more information see http://www.cl.cam.ac.uk/users/rja14/serpent.html + * + * 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 "serpent.h" +#include "serpentsboxes.h" + +void serpent_makekey(struct keyInstance *key, int keyLen, + uint8_t *keyMaterial) +{ + uint32_t i,j; + uint32_t w[132],k[132]; + + for(i=0; isubkeys[i][j] = k[4*i+j]; +} + +void serpent_encrypt(struct keyInstance *key, + uint32_t plaintext[4], + uint32_t ciphertext[4]) +{ + register uint32_t x0, x1, x2, x3; + register uint32_t y0, y1, y2, y3; + + x0=plaintext[0]; + x1=plaintext[1]; + x2=plaintext[2]; + x3=plaintext[3]; + + /* Start to encrypt the plaintext x */ + keying(x0, x1, x2, x3, key->subkeys[ 0]); + RND00(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 1]); + RND01(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 2]); + RND02(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 3]); + RND03(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 4]); + RND04(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 5]); + RND05(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 6]); + RND06(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 7]); + RND07(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 8]); + RND08(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[ 9]); + RND09(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[10]); + RND10(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[11]); + RND11(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[12]); + RND12(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[13]); + RND13(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[14]); + RND14(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[15]); + RND15(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[16]); + RND16(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[17]); + RND17(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[18]); + RND18(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[19]); + RND19(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[20]); + RND20(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[21]); + RND21(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[22]); + RND22(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[23]); + RND23(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[24]); + RND24(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[25]); + RND25(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[26]); + RND26(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[27]); + RND27(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[28]); + RND28(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[29]); + RND29(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[30]); + RND30(x0, x1, x2, x3, y0, y1, y2, y3); + transform(y0, y1, y2, y3, x0, x1, x2, x3); + keying(x0, x1, x2, x3, key->subkeys[31]); + RND31(x0, x1, x2, x3, y0, y1, y2, y3); + x0 = y0; x1 = y1; x2 = y2; x3 = y3; + keying(x0, x1, x2, x3, key->subkeys[32]); + /* The ciphertext is now in x */ + + ciphertext[0] = x0; + ciphertext[1] = x1; + ciphertext[2] = x2; + ciphertext[3] = x3; +} + +void serpent_decrypt(struct keyInstance *key, + uint32_t ciphertext[4], + uint32_t plaintext[4]) +{ + register uint32_t x0, x1, x2, x3; + register uint32_t y0, y1, y2, y3; + + x0=ciphertext[0]; + x1=ciphertext[1]; + x2=ciphertext[2]; + x3=ciphertext[3]; + + /* Start to decrypt the ciphertext x */ + keying(x0, x1, x2, x3, key->subkeys[32]); + InvRND31(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[31]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND30(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[30]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND29(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[29]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND28(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[28]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND27(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[27]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND26(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[26]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND25(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[25]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND24(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[24]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND23(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[23]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND22(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[22]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND21(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[21]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND20(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[20]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND19(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[19]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND18(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[18]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND17(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[17]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND16(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[16]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND15(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[15]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND14(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[14]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND13(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[13]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND12(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[12]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND11(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[11]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND10(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[10]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND09(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 9]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND08(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 8]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND07(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 7]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND06(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 6]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND05(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 5]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND04(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 4]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND03(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 3]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND02(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 2]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND01(x0, x1, x2, x3, y0, y1, y2, y3); + keying(y0, y1, y2, y3, key->subkeys[ 1]); + inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); + InvRND00(x0, x1, x2, x3, y0, y1, y2, y3); + x0 = y0; x1 = y1; x2 = y2; x3 = y3; + keying(x0, x1, x2, x3, key->subkeys[ 0]); + /* The plaintext is now in x */ + + plaintext[0] = x0; + plaintext[1] = x1; + plaintext[2] = x2; + plaintext[3] = x3; +} diff --git a/serpent.h b/serpent.h new file mode 100644 index 0000000..dbbd36e --- /dev/null +++ b/serpent.h @@ -0,0 +1,19 @@ +#ifndef serpent_h +#define serpent_h + +struct keyInstance { + uint32_t key[8]; /* The key in binary */ + uint32_t subkeys[33][4]; /* Serpent subkeys */ +}; + +/* Function protoypes */ +void serpent_makekey(struct keyInstance *key, int keyLen, + uint8_t *keyMaterial); + +void serpent_encrypt(struct keyInstance *key, uint32_t plaintext[4], + uint32_t ciphertext[4]); + +void serpent_decrypt(struct keyInstance *key, uint32_t ciphertext[4], + uint32_t plaintext[4]); + +#endif /* serpent_h */ diff --git a/serpentsboxes.h b/serpentsboxes.h new file mode 100644 index 0000000..57450f3 --- /dev/null +++ b/serpentsboxes.h @@ -0,0 +1,493 @@ +/* + * This file is + * Copyright (C) 1998 Ross Anderson, Eli Biham, Lars Knudsen + * + * For more information see http://www.cl.cam.ac.uk/users/rja14/serpent.html + * + * 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. + * + */ + + +/* S0: 3 8 15 1 10 6 5 11 14 13 4 2 7 0 9 12 */ + +/* depth = 5,7,4,2, Total gates=18 */ +#define RND00(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t05, t06, t07, t08, t09, t11, t12, t13, t14, t15, t17, t01;\ + t01 = b ^ c ; \ + t02 = a | d ; \ + t03 = a ^ b ; \ + z = t02 ^ t01; \ + t05 = c | z ; \ + t06 = a ^ d ; \ + t07 = b | c ; \ + t08 = d & t05; \ + t09 = t03 & t07; \ + y = t09 ^ t08; \ + t11 = t09 & y ; \ + t12 = c ^ d ; \ + t13 = t07 ^ t11; \ + t14 = b & t06; \ + t15 = t06 ^ t13; \ + w = ~ t15; \ + t17 = w ^ t14; \ + x = t12 ^ t17; } + +/* InvS0: 13 3 11 0 10 6 5 12 1 14 4 7 15 9 8 2 */ + +/* depth = 8,4,3,6, Total gates=19 */ +#define InvRND00(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t12, t13, t14, t15, t17, t18, t01;\ + t01 = c ^ d ; \ + t02 = a | b ; \ + t03 = b | c ; \ + t04 = c & t01; \ + t05 = t02 ^ t01; \ + t06 = a | t04; \ + y = ~ t05; \ + t08 = b ^ d ; \ + t09 = t03 & t08; \ + t10 = d | y ; \ + x = t09 ^ t06; \ + t12 = a | t05; \ + t13 = x ^ t12; \ + t14 = t03 ^ t10; \ + t15 = a ^ c ; \ + z = t14 ^ t13; \ + t17 = t05 & t13; \ + t18 = t14 | t17; \ + w = t15 ^ t18; } + +/* S1: 15 12 2 7 9 0 5 10 1 11 14 8 6 13 3 4 */ + +/* depth = 10,7,3,5, Total gates=18 */ +#define RND01(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t07, t08, t10, t11, t12, t13, t16, t17, t01;\ + t01 = a | d ; \ + t02 = c ^ d ; \ + t03 = ~ b ; \ + t04 = a ^ c ; \ + t05 = a | t03; \ + t06 = d & t04; \ + t07 = t01 & t02; \ + t08 = b | t06; \ + y = t02 ^ t05; \ + t10 = t07 ^ t08; \ + t11 = t01 ^ t10; \ + t12 = y ^ t11; \ + t13 = b & d ; \ + z = ~ t10; \ + x = t13 ^ t12; \ + t16 = t10 | x ; \ + t17 = t05 & t16; \ + w = c ^ t17; } + +/* InvS1: 5 8 2 14 15 6 12 3 11 4 7 9 1 13 10 0 */ + +/* depth = 7,4,5,3, Total gates=18 */ +#define InvRND01(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t14, t15, t17, t01;\ + t01 = a ^ b ; \ + t02 = b | d ; \ + t03 = a & c ; \ + t04 = c ^ t02; \ + t05 = a | t04; \ + t06 = t01 & t05; \ + t07 = d | t03; \ + t08 = b ^ t06; \ + t09 = t07 ^ t06; \ + t10 = t04 | t03; \ + t11 = d & t08; \ + y = ~ t09; \ + x = t10 ^ t11; \ + t14 = a | y ; \ + t15 = t06 ^ x ; \ + z = t01 ^ t04; \ + t17 = c ^ t15; \ + w = t14 ^ t17; } + +/* S2: 8 6 7 9 3 12 10 15 13 1 14 4 0 11 5 2 */ + +/* depth = 3,8,11,7, Total gates=16 */ +#define RND02(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t05, t06, t07, t08, t09, t10, t12, t13, t14, t01;\ + t01 = a | c ; \ + t02 = a ^ b ; \ + t03 = d ^ t01; \ + w = t02 ^ t03; \ + t05 = c ^ w ; \ + t06 = b ^ t05; \ + t07 = b | t05; \ + t08 = t01 & t06; \ + t09 = t03 ^ t07; \ + t10 = t02 | t09; \ + x = t10 ^ t08; \ + t12 = a | d ; \ + t13 = t09 ^ x ; \ + t14 = b ^ t13; \ + z = ~ t09; \ + y = t12 ^ t14; } + +/* InvS2: 12 9 15 4 11 14 1 2 0 3 6 13 5 8 10 7 */ + +/* depth = 3,6,8,3, Total gates=18 */ +#define InvRND02(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t12, t15, t16, t17, t01;\ + t01 = a ^ d ; \ + t02 = c ^ d ; \ + t03 = a & c ; \ + t04 = b | t02; \ + w = t01 ^ t04; \ + t06 = a | c ; \ + t07 = d | w ; \ + t08 = ~ d ; \ + t09 = b & t06; \ + t10 = t08 | t03; \ + t11 = b & t07; \ + t12 = t06 & t02; \ + z = t09 ^ t10; \ + x = t12 ^ t11; \ + t15 = c & z ; \ + t16 = w ^ x ; \ + t17 = t10 ^ t15; \ + y = t16 ^ t17; } + +/* S3: 0 15 11 8 12 9 6 3 13 1 2 4 10 7 5 14 */ + +/* depth = 8,3,5,5, Total gates=18 */ +#define RND03(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t13, t14, t15, t01;\ + t01 = a ^ c ; \ + t02 = a | d ; \ + t03 = a & d ; \ + t04 = t01 & t02; \ + t05 = b | t03; \ + t06 = a & b ; \ + t07 = d ^ t04; \ + t08 = c | t06; \ + t09 = b ^ t07; \ + t10 = d & t05; \ + t11 = t02 ^ t10; \ + z = t08 ^ t09; \ + t13 = d | z ; \ + t14 = a | t07; \ + t15 = b & t13; \ + y = t08 ^ t11; \ + w = t14 ^ t15; \ + x = t05 ^ t04; } + +/* InvS3: 0 9 10 7 11 14 6 13 3 5 12 2 4 8 15 1 */ + +/* depth = 3,6,4,4, Total gates=17 */ +#define InvRND03(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t07, t09, t11, t12, t13, t14, t16, t01;\ + t01 = c | d ; \ + t02 = a | d ; \ + t03 = c ^ t02; \ + t04 = b ^ t02; \ + t05 = a ^ d ; \ + t06 = t04 & t03; \ + t07 = b & t01; \ + y = t05 ^ t06; \ + t09 = a ^ t03; \ + w = t07 ^ t03; \ + t11 = w | t05; \ + t12 = t09 & t11; \ + t13 = a & y ; \ + t14 = t01 ^ t05; \ + x = b ^ t12; \ + t16 = b | t13; \ + z = t14 ^ t16; } + +/* S4: 1 15 8 3 12 0 11 6 2 5 4 10 9 14 7 13 */ + +/* depth = 6,7,5,3, Total gates=19 */ +#define RND04(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t12, t13, t14, t15, t16, t01;\ + t01 = a | b ; \ + t02 = b | c ; \ + t03 = a ^ t02; \ + t04 = b ^ d ; \ + t05 = d | t03; \ + t06 = d & t01; \ + z = t03 ^ t06; \ + t08 = z & t04; \ + t09 = t04 & t05; \ + t10 = c ^ t06; \ + t11 = b & c ; \ + t12 = t04 ^ t08; \ + t13 = t11 | t03; \ + t14 = t10 ^ t09; \ + t15 = a & t05; \ + t16 = t11 | t12; \ + y = t13 ^ t08; \ + x = t15 ^ t16; \ + w = ~ t14; } + +/* InvS4: 5 0 8 3 10 9 7 14 2 12 11 6 4 15 13 1 */ + +/* depth = 6,4,7,3, Total gates=17 */ +#define InvRND04(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t07, t09, t10, t11, t12, t13, t15, t01;\ + t01 = b | d ; \ + t02 = c | d ; \ + t03 = a & t01; \ + t04 = b ^ t02; \ + t05 = c ^ d ; \ + t06 = ~ t03; \ + t07 = a & t04; \ + x = t05 ^ t07; \ + t09 = x | t06; \ + t10 = a ^ t07; \ + t11 = t01 ^ t09; \ + t12 = d ^ t04; \ + t13 = c | t10; \ + z = t03 ^ t12; \ + t15 = a ^ t04; \ + y = t11 ^ t13; \ + w = t15 ^ t09; } + +/* S5: 15 5 2 11 4 10 9 12 0 3 14 8 13 6 7 1 */ + +/* depth = 4,6,8,6, Total gates=17 */ +#define RND05(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t14, t01;\ + t01 = b ^ d ; \ + t02 = b | d ; \ + t03 = a & t01; \ + t04 = c ^ t02; \ + t05 = t03 ^ t04; \ + w = ~ t05; \ + t07 = a ^ t01; \ + t08 = d | w ; \ + t09 = b | t05; \ + t10 = d ^ t08; \ + t11 = b | t07; \ + t12 = t03 | w ; \ + t13 = t07 | t10; \ + t14 = t01 ^ t11; \ + y = t09 ^ t13; \ + x = t07 ^ t08; \ + z = t12 ^ t14; } + +/* InvS5: 8 15 2 9 4 1 13 14 11 6 5 3 7 12 10 0 */ + +/* depth = 4,6,9,7, Total gates=17 */ +#define InvRND05(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t12, t13, t15, t16, t01;\ + t01 = a & d ; \ + t02 = c ^ t01; \ + t03 = a ^ d ; \ + t04 = b & t02; \ + t05 = a & c ; \ + w = t03 ^ t04; \ + t07 = a & w ; \ + t08 = t01 ^ w ; \ + t09 = b | t05; \ + t10 = ~ b ; \ + x = t08 ^ t09; \ + t12 = t10 | t07; \ + t13 = w | x ; \ + z = t02 ^ t12; \ + t15 = t02 ^ t13; \ + t16 = b ^ d ; \ + y = t16 ^ t15; } + +/* S6: 7 2 12 5 8 4 6 11 14 9 1 15 13 3 10 0 */ + +/* depth = 8,3,6,3, Total gates=19 */ +#define RND06(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t15, t17, t18, t01;\ + t01 = a & d ; \ + t02 = b ^ c ; \ + t03 = a ^ d ; \ + t04 = t01 ^ t02; \ + t05 = b | c ; \ + x = ~ t04; \ + t07 = t03 & t05; \ + t08 = b & x ; \ + t09 = a | c ; \ + t10 = t07 ^ t08; \ + t11 = b | d ; \ + t12 = c ^ t11; \ + t13 = t09 ^ t10; \ + y = ~ t13; \ + t15 = x & t03; \ + z = t12 ^ t07; \ + t17 = a ^ b ; \ + t18 = y ^ t15; \ + w = t17 ^ t18; } + +/* InvS6: 15 10 1 13 5 3 6 0 4 9 14 7 2 12 8 11 */ + +/* depth = 5,3,8,6, Total gates=19 */ +#define InvRND06(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t12, t13, t14, t15, t16, t17, t01;\ + t01 = a ^ c ; \ + t02 = ~ c ; \ + t03 = b & t01; \ + t04 = b | t02; \ + t05 = d | t03; \ + t06 = b ^ d ; \ + t07 = a & t04; \ + t08 = a | t02; \ + t09 = t07 ^ t05; \ + x = t06 ^ t08; \ + w = ~ t09; \ + t12 = b & w ; \ + t13 = t01 & t05; \ + t14 = t01 ^ t12; \ + t15 = t07 ^ t13; \ + t16 = d | t02; \ + t17 = a ^ x ; \ + z = t17 ^ t15; \ + y = t16 ^ t14; } + +/* S7: 1 13 15 0 14 8 2 11 7 4 12 10 9 3 5 6 */ + +/* depth = 10,7,10,4, Total gates=19 */ +#define RND07(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t13, t14, t15, t16, t17, t01;\ + t01 = a & c ; \ + t02 = ~ d ; \ + t03 = a & t02; \ + t04 = b | t01; \ + t05 = a & b ; \ + t06 = c ^ t04; \ + z = t03 ^ t06; \ + t08 = c | z ; \ + t09 = d | t05; \ + t10 = a ^ t08; \ + t11 = t04 & z ; \ + x = t09 ^ t10; \ + t13 = b ^ x ; \ + t14 = t01 ^ x ; \ + t15 = c ^ t05; \ + t16 = t11 | t13; \ + t17 = t02 | t14; \ + w = t15 ^ t17; \ + y = a ^ t16; } + +/* InvS7: 3 0 6 13 9 14 15 8 5 12 11 7 10 1 4 2 */ + +/* depth = 9,7,3,3, Total gates=18 */ +#define InvRND07(a,b,c,d,w,x,y,z) \ + { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t13, t14, t15, t16, t01;\ + t01 = a & b ; \ + t02 = a | b ; \ + t03 = c | t01; \ + t04 = d & t02; \ + z = t03 ^ t04; \ + t06 = b ^ t04; \ + t07 = d ^ z ; \ + t08 = ~ t07; \ + t09 = t06 | t08; \ + t10 = b ^ d ; \ + t11 = a | d ; \ + x = a ^ t09; \ + t13 = c ^ t06; \ + t14 = c & t11; \ + t15 = d | x ; \ + t16 = t01 | t10; \ + w = t13 ^ t15; \ + y = t14 ^ t16; } + +#define RND08(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h) +#define RND09(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h) +#define RND10(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h) +#define RND11(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h) +#define RND12(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h) +#define RND13(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h) +#define RND14(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h) +#define RND15(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h) +#define RND16(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h) +#define RND17(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h) +#define RND18(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h) +#define RND19(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h) +#define RND20(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h) +#define RND21(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h) +#define RND22(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h) +#define RND23(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h) +#define RND24(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h) +#define RND25(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h) +#define RND26(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h) +#define RND27(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h) +#define RND28(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h) +#define RND29(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h) +#define RND30(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h) +#define RND31(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h) + +#define InvRND08(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h) +#define InvRND09(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h) +#define InvRND10(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h) +#define InvRND11(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h) +#define InvRND12(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h) +#define InvRND13(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h) +#define InvRND14(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h) +#define InvRND15(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h) +#define InvRND16(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h) +#define InvRND17(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h) +#define InvRND18(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h) +#define InvRND19(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h) +#define InvRND20(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h) +#define InvRND21(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h) +#define InvRND22(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h) +#define InvRND23(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h) +#define InvRND24(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h) +#define InvRND25(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h) +#define InvRND26(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h) +#define InvRND27(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h) +#define InvRND28(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h) +#define InvRND29(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h) +#define InvRND30(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h) +#define InvRND31(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h) + +/* Linear transformations and key mixing: */ + +#define ROL(x,n) ((((unsigned long)(x))<<(n))| \ + (((unsigned long)(x))>>(32-(n)))) +#define ROR(x,n) ((((unsigned long)(x))<<(32-(n)))| \ + (((unsigned long)(x))>>(n))) + +#define transform(x0, x1, x2, x3, y0, y1, y2, y3) \ + y0 = ROL(x0, 13); \ + y2 = ROL(x2, 3); \ + y1 = x1 ^ y0 ^ y2; \ + y3 = x3 ^ y2 ^ ((unsigned long)y0)<<3; \ + y1 = ROL(y1, 1); \ + y3 = ROL(y3, 7); \ + y0 = y0 ^ y1 ^ y3; \ + y2 = y2 ^ y3 ^ ((unsigned long)y1<<7); \ + y0 = ROL(y0, 5); \ + y2 = ROL(y2, 22) + +#define inv_transform(x0, x1, x2, x3, y0, y1, y2, y3) \ + y2 = ROR(x2, 22);\ + y0 = ROR(x0, 5); \ + y2 = y2 ^ x3 ^ ((unsigned long)x1<<7); \ + y0 = y0 ^ x1 ^ x3; \ + y3 = ROR(x3, 7); \ + y1 = ROR(x1, 1); \ + y3 = y3 ^ y2 ^ ((unsigned long)y0)<<3; \ + y1 = y1 ^ y0 ^ y2; \ + y2 = ROR(y2, 3); \ + y0 = ROR(y0, 13) + +#define keying(x0, x1, x2, x3, subkey) \ + x0^=subkey[0];x1^=subkey[1]; \ + x2^=subkey[2];x3^=subkey[3] + +/* PHI: Constant used in the key schedule */ +#define PHI 0x9e3779b9L diff --git a/site.c b/site.c new file mode 100644 index 0000000..c63ca8b --- /dev/null +++ b/site.c @@ -0,0 +1,1161 @@ +/* site.c - manage communication with a remote network site */ + +#include +#include + +#include "secnet.h" +#include "util.h" + +#define SETUP_BUFFER_LEN 2048 + +#define DEFAULT_KEY_LIFETIME 15000 +#define DEFAULT_SETUP_RETRIES 5 +#define DEFAULT_SETUP_TIMEOUT 500 +#define DEFAULT_WAIT_TIME 10000 + +/* Each site can be in one of several possible states. */ + +/* States: + SITE_STOP - nothing is allowed to happen; tunnel is down; + all session keys have been erased + -> SITE_RUN upon external instruction + SITE_RUN - site up, maybe with valid key + -> SITE_RESOLVE upon outgoing packet and no valid key + we start name resolution for the other end of the tunnel + -> SITE_SENTMSG2 upon valid incoming message 1 and suitable time + we send an appropriate message 2 + SITE_RESOLVE - waiting for name resolution + -> SITE_SENTMSG1 upon successful resolution + we send an appropriate message 1 + -> SITE_SENTMSG2 upon valid incoming message 1 (then abort resolution) + we abort resolution and + -> SITE_WAIT on timeout or resolution failure + SITE_SENTMSG1 + -> SITE_SENTMSG2 upon valid incoming message 1 from higher priority end + -> SITE_SENTMSG3 upon valid incoming message 2 + -> SITE_WAIT on timeout + SITE_SENTMSG2 + -> SITE_SENTMSG4 upon valid incoming message 3 + -> SITE_WAIT on timeout + SITE_SENTMSG3 + -> SITE_SENTMSG5 upon valid incoming message 4 + -> SITE_WAIT on timeout + SITE_SENTMSG4 + -> SITE_RUN upon valid incoming message 5 + -> SITE_WAIT on timeout + SITE_SENTMSG5 + -> SITE_RUN upon valid incoming message 6 + -> SITE_WAIT on timeout + SITE_WAIT - failed to establish key; do nothing for a while + -> SITE_RUN on timeout + */ + +#define SITE_STOP 0 +#define SITE_RUN 1 +#define SITE_RESOLVE 2 +#define SITE_SENTMSG1 3 +#define SITE_SENTMSG2 4 +#define SITE_SENTMSG3 5 +#define SITE_SENTMSG4 6 +#define SITE_SENTMSG5 7 +#define SITE_WAIT 8 + +static string_t state_name(uint32_t state) +{ + switch (state) { + case 0: return "SITE_STOP"; + case 1: return "SITE_RUN"; + case 2: return "SITE_RESOLVE"; + case 3: return "SITE_SENTMSG1"; + case 4: return "SITE_SENTMSG2"; + case 5: return "SITE_SENTMSG3"; + case 6: return "SITE_SENTMSG4"; + case 7: return "SITE_SENTMSG5"; + case 8: return "SITE_WAIT"; + default: return "*bad state*"; + } +} + +#define LABEL_MSG0 0x00020200 +#define LABEL_MSG1 0x01010101 +#define LABEL_MSG2 0x02020202 +#define LABEL_MSG3 0x03030303 +#define LABEL_MSG4 0x04040404 +#define LABEL_MSG5 0x05050505 +#define LABEL_MSG6 0x06060606 +#define LABEL_MSG7 0x07070707 +#define LABEL_MSG8 0x08080808 +#define LABEL_MSG9 0x09090909 + +#define NONCELEN 8 + +#define LOG_UNEXPECTED 0x00000001 +#define LOG_SETUP_INIT 0x00000002 +#define LOG_SETUP_TIMEOUT 0x00000004 +#define LOG_ACTIVATE_KEY 0x00000008 +#define LOG_TIMEOUT_KEY 0x00000010 +#define LOG_SECURITY 0x00000020 +#define LOG_STATE 0x00000040 +#define LOG_DROP 0x00000080 +#define LOG_DUMP 0x00000100 +#define LOG_ERROR 0x00000400 + +struct site { + closure_t cl; + struct site_if ops; +/* configuration information */ + string_t localname; + string_t remotename; + string_t address; /* DNS name for bootstrapping, optional */ + int remoteport; + struct netlink_if *netlink; + struct comm_if *comm; + struct resolver_if *resolver; + struct log_if *log; + struct random_if *random; + struct rsaprivkey_if *privkey; + struct subnet_list remotenets; + struct rsapubkey_if *pubkey; + struct transform_if *transform; + struct dh_if *dh; + struct hash_if *hash; + void *netlink_cid; + + uint32_t setup_retries; /* How many times to send setup packets */ + uint32_t setup_timeout; /* Initial timeout for setup packets */ + uint32_t wait_timeout; /* How long to wait if setup unsuccessful */ + uint32_t key_lifetime; /* How long a key lasts once set up */ + + uint8_t *setupsig; /* Expected signature of incoming MSG1 packets */ + uint32_t setupsiglen; /* Allows us to discard packets quickly if + they are not for us */ + bool_t setup_priority; /* Do we have precedence if both sites emit + message 1 simultaneously? */ + uint32_t log_events; + +/* runtime information */ + uint32_t state; + uint64_t now; /* Most recently seen time */ + + uint32_t remote_session_id; + struct transform_inst_if *current_transform; + bool_t current_valid; + uint64_t current_key_timeout; /* End of life of current key */ + struct sockaddr_in peer; /* Current address of peer */ + bool_t peer_valid; /* Peer address becomes invalid when key times out, + but only if we have a DNS name for our peer */ + + uint32_t setup_session_id; + struct sockaddr_in setup_peer; + uint8_t localN[NONCELEN]; /* Nonces for key exchange */ + uint8_t remoteN[NONCELEN]; + struct buffer_if buffer; /* Current outgoing key exchange packet */ + uint32_t retries; /* Number of retries remaining */ + uint64_t timeout; /* Timeout for current state */ + uint8_t *dhsecret; + uint8_t *sharedsecret; + + struct transform_inst_if *new_transform; /* For key setup/verify */ +}; + +static void slog(struct site *st, uint32_t event, string_t msg, ...) +{ + va_list ap; + uint8_t buf[240]; + + va_start(ap,msg); + + if (event&st->log_events) { + vsnprintf(buf,240,msg,ap); + st->log->log(st->log->st,0,"%s<->%s: %s",st->localname,st->remotename, + buf); + } + va_end(ap); +} + +static void enter_state_run(struct site *st); +static bool_t enter_state_resolve(struct site *st); +static bool_t enter_state_sentmsg1(struct site *st); +static bool_t enter_state_sentmsg2(struct site *st); +static bool_t enter_state_sentmsg3(struct site *st); +static bool_t enter_state_sentmsg4(struct site *st); +static bool_t enter_state_sentmsg5(struct site *st); +static void enter_state_wait(struct site *st); + +#define CHECK_AVAIL(b,l) do { if ((b)->size<(l)) return False; } while(0) +#define CHECK_EMPTY(b) do { if ((b)->size!=0) return False; } while(0) +#define CHECK_TYPE(b,t) do { uint32_t type; \ + CHECK_AVAIL((b),4); \ + type=*(uint32_t *)buf_unprepend((b),4); \ + if (type!=(t)) return False; } while(0) + +struct msg { + uint8_t *hashstart; + uint32_t dest; + uint32_t source; + uint32_t remlen; + uint8_t *remote; + uint32_t loclen; + uint8_t *local; + uint8_t *nR; + uint8_t *nL; + uint32_t pklen; + uint8_t *pk; + uint32_t hashlen; + uint32_t siglen; + uint8_t *sig; +}; + +/* Build any of msg1 to msg4. msg5 and msg6 are built from the inside out + using a transform. */ +static bool_t generate_msg(struct site *st, uint32_t type, string_t what) +{ + void *hst; + uint8_t *hash=alloca(st->hash->len); + string_t dhpub, sig; + + st->retries=st->setup_retries; + BUF_ALLOC(&st->buffer,what); + buffer_init(&st->buffer,0); + *(uint32_t *)buf_append(&st->buffer,4)= + (type==LABEL_MSG1?0:st->setup_session_id); + *(uint32_t *)buf_append(&st->buffer,4)=(uint32_t)st; + *(uint32_t *)buf_append(&st->buffer,4)=type; + buf_append_string(&st->buffer,st->localname); + buf_append_string(&st->buffer,st->remotename); + memcpy(buf_append(&st->buffer,NONCELEN),st->localN,NONCELEN); + if (type==LABEL_MSG1) return True; + memcpy(buf_append(&st->buffer,NONCELEN),st->remoteN,NONCELEN); + if (type==LABEL_MSG2) return True; + dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len); + buf_append_string(&st->buffer,dhpub); + free(dhpub); + hst=st->hash->init(); + st->hash->update(hst,st->buffer.start,st->buffer.size); + st->hash->final(hst,hash); + sig=st->privkey->sign(st->privkey->st,hash,st->hash->len); + buf_append_string(&st->buffer,sig); + free(sig); + return True; +} + +static bool_t unpick_msg(struct site *st, uint32_t type, + struct buffer_if *msg, struct msg *m) +{ + m->hashstart=msg->start; + CHECK_AVAIL(msg,4); + m->dest=*(uint32_t *)buf_unprepend(msg,4); + CHECK_AVAIL(msg,4); + m->source=*(uint32_t *)buf_unprepend(msg,4); + CHECK_TYPE(msg,type); + CHECK_AVAIL(msg,2); + m->remlen=ntohs(*(uint16_t *)buf_unprepend(msg,2)); + CHECK_AVAIL(msg,m->remlen); + m->remote=buf_unprepend(msg,m->remlen); + CHECK_AVAIL(msg,2); + m->loclen=ntohs(*(uint16_t *)buf_unprepend(msg,2)); + CHECK_AVAIL(msg,m->loclen); + m->local=buf_unprepend(msg,m->loclen); + CHECK_AVAIL(msg,NONCELEN); + m->nR=buf_unprepend(msg,NONCELEN); + if (type==LABEL_MSG1) { + CHECK_EMPTY(msg); + return True; + } + CHECK_AVAIL(msg,NONCELEN); + m->nL=buf_unprepend(msg,NONCELEN); + if (type==LABEL_MSG2) { + CHECK_EMPTY(msg); + return True; + } + CHECK_AVAIL(msg,2); + m->pklen=ntohs(*(uint16_t *)buf_unprepend(msg,2)); + CHECK_AVAIL(msg,m->pklen); + m->pk=buf_unprepend(msg,m->pklen); + m->hashlen=msg->start-m->hashstart; + CHECK_AVAIL(msg,2); + m->siglen=ntohs(*(uint16_t *)buf_unprepend(msg,2)); + CHECK_AVAIL(msg,m->siglen); + m->sig=buf_unprepend(msg,m->siglen); + CHECK_EMPTY(msg); + return True; +} + +static bool_t generate_msg1(struct site *st) +{ + st->random->generate(st->random->st,NONCELEN,st->localN); + return generate_msg(st,LABEL_MSG1,"site:MSG1"); +} + +static bool_t process_msg1(struct site *st, struct buffer_if *msg1, + struct sockaddr_in *src) +{ + struct msg m; + + /* We've already determined we're in an appropriate state to + process an incoming MSG1, and that the MSG1 has correct values + of A and B. */ + + if (!unpick_msg(st,LABEL_MSG1,msg1,&m)) return False; + + /* XXX save src as our peer address here? */ + st->setup_peer=*src; + + st->setup_session_id=m.source; + memcpy(st->remoteN,m.nR,NONCELEN); + return True; +} + +static bool_t generate_msg2(struct site *st) +{ + st->random->generate(st->random->st,NONCELEN,st->localN); + return generate_msg(st,LABEL_MSG2,"site:MSG2"); +} + +static bool_t process_msg2(struct site *st, struct buffer_if *msg2, + struct sockaddr_in *src) +{ + struct msg m; + + if (!unpick_msg(st,LABEL_MSG2,msg2,&m)) return False; + + /* Check that the site names and our nonce have been sent + back correctly, and then store our peer's nonce. */ + if (memcmp(m.remote,st->remotename,strlen(st->remotename)!=0)) { + slog(st,LOG_SECURITY,"msg2: bad B (remote site name)"); + return False; + } + if (memcmp(m.local,st->localname,strlen(st->localname)!=0)) { + slog(st,LOG_SECURITY,"msg2: bad A (local site name)"); + return False; + } + if (memcmp(m.nL,st->localN,NONCELEN)!=0) { + slog(st,LOG_SECURITY,"msg2: bad nA (locally generated nonce)"); + return False; + } + st->setup_session_id=m.source; + memcpy(st->remoteN,m.nR,NONCELEN); + return True; +} + +static bool_t generate_msg3(struct site *st) +{ + /* Now we have our nonce and their nonce. Think of a secret key, + and create message number 3. */ + st->random->generate(st->random->st,st->dh->len,st->dhsecret); + return generate_msg(st,LABEL_MSG3,"site:MSG3"); +} + +static bool_t process_msg3(struct site *st, struct buffer_if *msg3, + struct sockaddr_in *src) +{ + struct msg m; + uint8_t *hash=alloca(st->hash->len); + void *hst; + + if (!unpick_msg(st,LABEL_MSG3,msg3,&m)) return False; + + /* Check that the site names and nonces have been sent back + correctly */ + if (memcmp(m.remote,st->remotename,strlen(st->remotename)!=0)) { + slog(st,LOG_SECURITY,"msg3: bad A (remote site name)"); + return False; + } + if (memcmp(m.local,st->localname,strlen(st->localname)!=0)) { + slog(st,LOG_SECURITY,"msg3: bad B (local site name)"); + return False; + } + if (memcmp(m.nR,st->remoteN,NONCELEN)!=0) { + slog(st,LOG_SECURITY,"msg3: bad nA (remotely generated nonce)"); + return False; + } + if (memcmp(m.nL,st->localN,NONCELEN)!=0) { + slog(st,LOG_SECURITY,"msg3: bad nB (locally generated nonce)"); + return False; + } + + /* Check signature and store g^x mod m */ + hst=st->hash->init(); + st->hash->update(hst,m.hashstart,m.hashlen); + st->hash->final(hst,hash); + /* Terminate signature with a '0' - cheating, but should be ok */ + m.sig[m.siglen]=0; + if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) { + slog(st,LOG_SECURITY,"msg3 signature failed check!"); + return False; + } + + /* Terminate their DH public key with a '0' */ + m.pk[m.pklen]=0; + /* Invent our DH secret key */ + st->random->generate(st->random->st,st->dh->len,st->dhsecret); + + /* Generate the shared key */ + st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->len,m.pk, + st->sharedsecret,st->transform->keylen); + + /* Set up the transform */ + st->new_transform->setkey(st->new_transform->st,st->sharedsecret, + st->transform->keylen); + + return True; +} + +static bool_t generate_msg4(struct site *st) +{ + /* We have both nonces, their public key and our private key. Generate + our public key, sign it and send it to them. */ + return generate_msg(st,LABEL_MSG4,"site:MSG4"); +} + +static bool_t process_msg4(struct site *st, struct buffer_if *msg4, + struct sockaddr_in *src) +{ + struct msg m; + uint8_t *hash=alloca(st->hash->len); + void *hst; + + if (!unpick_msg(st,LABEL_MSG4,msg4,&m)) return False; + + /* Check that the site names and nonces have been sent back + correctly */ + if (memcmp(m.remote,st->remotename,strlen(st->remotename)!=0)) { + slog(st,LOG_SECURITY,"msg4: bad B (remote site name)"); + return False; + } + if (memcmp(m.local,st->localname,strlen(st->localname)!=0)) { + slog(st,LOG_SECURITY,"msg4: bad A (local site name)"); + return False; + } + if (memcmp(m.nR,st->remoteN,NONCELEN)!=0) { + slog(st,LOG_SECURITY,"msg4: bad nB (remotely generated nonce)"); + return False; + } + if (memcmp(m.nL,st->localN,NONCELEN)!=0) { + slog(st,LOG_SECURITY,"msg4: bad nA (locally generated nonce)"); + return False; + } + + /* Check signature and store g^x mod m */ + hst=st->hash->init(); + st->hash->update(hst,m.hashstart,m.hashlen); + st->hash->final(hst,hash); + /* Terminate signature with a '0' - cheating, but should be ok */ + m.sig[m.siglen]=0; + if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) { + slog(st,LOG_SECURITY,"msg4 signature failed check!"); + return False; + } + + /* Terminate their DH public key with a '0' */ + m.pk[m.pklen]=0; + /* Generate the shared key */ + st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->len,m.pk, + st->sharedsecret,st->transform->keylen); + /* Set up the transform */ + st->new_transform->setkey(st->new_transform->st,st->sharedsecret, + st->transform->keylen); + + return True; +} + +static bool_t generate_msg5(struct site *st) +{ + string_t transform_err; + + BUF_ALLOC(&st->buffer,"site:MSG5"); + /* We are going to add three words to the transformed message */ + buffer_init(&st->buffer,st->transform->max_start_pad+(4*3)); + *(uint32_t *)buf_append(&st->buffer,4)=LABEL_MSG5; + st->new_transform->forwards(st->new_transform->st,&st->buffer, + &transform_err); + *(uint32_t *)buf_prepend(&st->buffer,4)=LABEL_MSG5; + *(uint32_t *)buf_prepend(&st->buffer,4)=(uint32_t)st; + *(uint32_t *)buf_prepend(&st->buffer,4)=st->setup_session_id; + + st->retries=st->setup_retries; + return True; +} + +struct msg0 { + uint32_t dest; + uint32_t source; + uint32_t type; +}; + +static bool_t unpick_msg0(struct site *st, struct buffer_if *msg0, + struct msg0 *m) +{ + CHECK_AVAIL(msg0,4); + m->dest=*(uint32_t *)buf_unprepend(msg0,4); + CHECK_AVAIL(msg0,4); + m->source=*(uint32_t *)buf_unprepend(msg0,4); + CHECK_AVAIL(msg0,4); + m->type=*(uint32_t *)buf_unprepend(msg0,4); + return True; + /* Leaves transformed part of buffer untouched */ +} + +static bool_t process_msg5(struct site *st, struct buffer_if *msg5, + struct sockaddr_in *src) +{ + struct msg0 m; + string_t transform_err; + + if (!unpick_msg0(st,msg5,&m)) return False; + + if (st->new_transform->reverse(st->new_transform->st, + msg5,&transform_err)) { + /* There's a problem */ + slog(st,LOG_SECURITY,"process_msg5: transform: %s",transform_err); + return False; + } + /* Buffer should now contain untransformed PING packet data */ + CHECK_AVAIL(msg5,4); + if ((*(uint32_t *)buf_unprepend(msg5,4))!=LABEL_MSG5) { + slog(st,LOG_SECURITY,"MSG5/PING packet contained invalid data"); + return False; + } + CHECK_EMPTY(msg5); + return True; +} + +static bool_t generate_msg6(struct site *st) +{ + string_t transform_err; + + BUF_ALLOC(&st->buffer,"site:MSG6"); + /* We are going to add three words to the transformed message */ + buffer_init(&st->buffer,st->transform->max_start_pad+(4*3)); + *(uint32_t *)buf_append(&st->buffer,4)=LABEL_MSG6; + st->new_transform->forwards(st->new_transform->st,&st->buffer, + &transform_err); + *(uint32_t *)buf_prepend(&st->buffer,4)=LABEL_MSG6; + *(uint32_t *)buf_prepend(&st->buffer,4)=(uint32_t)st; + *(uint32_t *)buf_prepend(&st->buffer,4)=st->setup_session_id; + + st->retries=1; /* Peer will retransmit MSG5 if necessary */ + return True; +} + +static bool_t process_msg6(struct site *st, struct buffer_if *msg6, + struct sockaddr_in *src) +{ + struct msg0 m; + string_t transform_err; + + if (!unpick_msg0(st,msg6,&m)) return False; + + if (st->new_transform->reverse(st->new_transform->st, + msg6,&transform_err)) { + /* There's a problem */ + slog(st,LOG_SECURITY,"process_msg6: transform: %s",transform_err); + return False; + } + /* Buffer should now contain untransformed PING packet data */ + CHECK_AVAIL(msg6,4); + if ((*(uint32_t *)buf_unprepend(msg6,4))!=LABEL_MSG6) { + slog(st,LOG_SECURITY,"MSG6/PONG packet contained invalid data"); + return False; + } + CHECK_EMPTY(msg6); + return True; +} + +static bool_t process_msg0(struct site *st, struct buffer_if *msg0, + struct sockaddr_in *src) +{ + struct msg0 m; + string_t transform_err; + uint32_t type; + + if (!st->current_valid) { + slog(st,LOG_DROP,"incoming message but no current key -> dropping"); + if (st->state==SITE_RUN) { + slog(st,LOG_SETUP_INIT|LOG_STATE, + "now initiating setup of new key"); + return enter_state_resolve(st); + } + return False; + } + + if (!unpick_msg0(st,msg0,&m)) return False; + + if (st->current_transform->reverse(st->current_transform->st, + msg0,&transform_err)) { + /* There's a problem */ + slog(st,LOG_SECURITY,"transform: %s",transform_err); + return False; + } + CHECK_AVAIL(msg0,4); + type=*(uint32_t *)buf_unprepend(msg0,4); + switch(type) { + case LABEL_MSG9: + /* Deliver to netlink layer */ + st->netlink->deliver(st->netlink->st,st->netlink_cid,msg0); + return True; + break; + default: + slog(st,LOG_SECURITY,"incoming message of type %08x (unknown)",type); + break; + } + return False; +} + +static void dump_packet(struct site *st, struct buffer_if *buf, + struct sockaddr_in *addr, bool_t incoming) +{ + uint32_t dest=*(uint32_t *)buf->start; + uint32_t source=*(uint32_t *)(buf->start+4); + uint32_t msgtype=*(uint32_t *)(buf->start+8); + + if (st->log_events & LOG_DUMP) + log(st->log,0,"(%s,%s): %s: %08x<-%08x: %08x:", + st->localname,st->remotename,incoming?"incoming":"outgoing", + dest,source,msgtype); +} + +static uint32_t site_status(void *st) +{ + return 0; +} + +static bool_t send_msg(struct site *st) +{ + if (st->retries>0) { + dump_packet(st,&st->buffer,&st->setup_peer,False); + st->comm->sendmsg(st->comm->st,&st->buffer,&st->setup_peer); + st->timeout=st->now+st->setup_timeout; + st->retries--; + return True; + } else { + slog(st,LOG_SETUP_TIMEOUT,"timed out sending key setup packet"); + enter_state_wait(st); + return False; + } +} + +static void site_resolve_callback(void *sst, struct in_addr *address) +{ + struct site *st=sst; + + if (st->state!=SITE_RESOLVE) { + slog(st,LOG_UNEXPECTED,"site_resolve_callback called unexpectedly"); + return; + } + if (address) { + memset(&st->setup_peer,0,sizeof(st->setup_peer)); + st->setup_peer.sin_family=AF_INET; + st->setup_peer.sin_port=htons(st->remoteport); + st->setup_peer.sin_addr=*address; + enter_state_sentmsg1(st); + } else { + /* Resolution failed */ + slog(st,LOG_ERROR,"resolution of %s failed",st->address); + enter_state_run(st); + } +} + +static void activate_new_key(struct site *st) +{ + struct transform_inst_if *t; + + t=st->current_transform; + st->current_transform=st->new_transform; + st->new_transform=t; + + t->delkey(t->st); + st->state=SITE_RUN; + st->timeout=0; + st->current_valid=True; + st->current_key_timeout=st->now+st->key_lifetime; + st->peer=st->setup_peer; + st->peer_valid=True; + st->remote_session_id=st->setup_session_id; + + slog(st,LOG_ACTIVATE_KEY,"new key activated"); +} + +static void state_assert(struct site *st, bool_t ok) +{ + if (!ok) fatal("state_assert\n"); +} + +static void enter_state_stop(struct site *st) +{ + st->state=SITE_STOP; + st->timeout=0; + st->current_transform->delkey(st->current_transform->st); + st->current_valid=False; + st->current_key_timeout=0; + + st->peer_valid=False; + + st->new_transform->delkey(st->new_transform->st); +} + +static void enter_state_run(struct site *st) +{ + slog(st,LOG_STATE,"entering state RUN"); + st->state=SITE_RUN; + st->timeout=0; + /* XXX get rid of key setup data */ +} + +static bool_t enter_state_resolve(struct site *st) +{ + state_assert(st,st->state==SITE_RUN); + slog(st,LOG_STATE,"entering state RESOLVE"); + st->state=SITE_RESOLVE; + st->resolver->request(st->resolver->st,st->address, + site_resolve_callback,st); + return True; +} + +static bool_t enter_state_sentmsg1(struct site *st) +{ + state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE); + slog(st,LOG_STATE,"entering state SENTMSG1"); + if (generate_msg1(st) && send_msg(st)) { + st->state=SITE_SENTMSG1; + return True; + } + slog(st,LOG_ERROR,"error entering state SENTMSG1"); + st->buffer.free=False; /* Can't tell which it was, but enter_state_wait() + will do a BUF_FREE() */ + enter_state_wait(st); + return False; +} + +static bool_t enter_state_sentmsg2(struct site *st) +{ + state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE || + st->state==SITE_SENTMSG1 || st->state==SITE_WAIT); + slog(st,LOG_STATE,"entering state SENTMSG2"); + if (generate_msg2(st) && send_msg(st)) { + st->state=SITE_SENTMSG2; + return True; + } + slog(st,LOG_ERROR,"error entering state SENTMSG2"); + st->buffer.free=False; + enter_state_wait(st); + return False; +} + +static bool_t enter_state_sentmsg3(struct site *st) +{ + state_assert(st,st->state==SITE_SENTMSG1); + slog(st,LOG_STATE,"entering state SENTMSG3"); + BUF_FREE(&st->buffer); /* Free message 1 */ + if (generate_msg3(st) && send_msg(st)) { + st->state=SITE_SENTMSG3; + return True; + } + slog(st,LOG_ERROR,"error entering state SENTMSG3"); + st->buffer.free=False; + enter_state_wait(st); + return False; +} + +static bool_t enter_state_sentmsg4(struct site *st) +{ + state_assert(st,st->state==SITE_SENTMSG2); + slog(st,LOG_STATE,"entering state SENTMSG4"); + BUF_FREE(&st->buffer); /* Free message 2 */ + if (generate_msg4(st) && send_msg(st)) { + st->state=SITE_SENTMSG4; + return True; + } + slog(st,LOG_ERROR,"error entering state SENTMSG4"); + st->buffer.free=False; + enter_state_wait(st); + return False; +} + +static bool_t enter_state_sentmsg5(struct site *st) +{ + state_assert(st,st->state==SITE_SENTMSG3); + slog(st,LOG_STATE,"entering state SENTMSG5"); + BUF_FREE(&st->buffer); /* Free message 3 */ + + if (generate_msg5(st) && send_msg(st)) { + st->state=SITE_SENTMSG5; + return True; + } + slog(st,LOG_ERROR,"error entering state SENTMSG5"); + st->buffer.free=False; + enter_state_wait(st); + + return False; +} + +static bool_t send_msg6(struct site *st) +{ + state_assert(st,st->state==SITE_SENTMSG4); + slog(st,LOG_STATE,"entering state RUN after sending msg6"); + BUF_FREE(&st->buffer); /* Free message 4 */ + if (generate_msg6(st) && send_msg(st)) { + BUF_FREE(&st->buffer); /* Never reused */ + st->timeout=0; /* Never retransmit */ + activate_new_key(st); + return True; + } + slog(st,LOG_ERROR,"error entering state RUN after sending msg6"); + st->buffer.free=False; + enter_state_wait(st); + return False; +} + +/* We go into this state if our peer becomes uncommunicative. Similar to + the "stop" state, we forget all session keys for a while, before + re-entering the "run" state. */ +static void enter_state_wait(struct site *st) +{ + slog(st,LOG_STATE,"entering state WAIT"); + st->timeout=st->now+st->wait_timeout; + st->state=SITE_WAIT; + st->peer_valid=False; + BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */ + /* XXX Erase keys etc. */ +} + +static int site_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, + int *timeout_io, const struct timeval *tv_now, + uint64_t *now) +{ + struct site *st=sst; + + *nfds_io=0; /* We don't use any file descriptors */ + st->now=*now; + + /* Work out when our next timeout is. The earlier of 'timeout' or + 'current_key_timeout'. A stored value of '0' indicates no timeout + active. */ + if (st->timeout && st->timeout-*now < *timeout_io) { + *timeout_io=st->timeout-*now; + } + + if (st->current_key_timeout && st->current_key_timeout-*now < *timeout_io) + *timeout_io=st->current_key_timeout-*now; + + return 0; /* success */ +} + +/* NB site_afterpoll will be called before site_beforepoll is ever called */ +static void site_afterpoll(void *sst, struct pollfd *fds, int nfds, + const struct timeval *tv_now, uint64_t *now) +{ + struct site *st=sst; + + st->now=*now; + if (st->timeout && *now>st->timeout) { + /* Do stuff */ + st->timeout=0; + if (st->state>=SITE_SENTMSG1 && st->state<=SITE_SENTMSG5) + send_msg(st); + else if (st->state==SITE_WAIT) { + enter_state_run(st); + } else { + slog(st,LOG_ERROR,"site_afterpoll: unexpected timeout, state=%d", + st->state); + } + } + if (st->current_key_timeout && *now>st->current_key_timeout) { + slog(st,LOG_TIMEOUT_KEY,"maximum key life exceeded; session closed"); + st->current_valid=False; + st->current_transform->delkey(st->current_transform->st); + st->current_key_timeout=0; + } +} + +/* This function is called by the netlink device to deliver packets + intended for the remote network. The packet is in "raw" wire + format, but is guaranteed to be word-aligned. */ +static void site_outgoing(void *sst, void *cid, struct buffer_if *buf) +{ + struct site *st=sst; + string_t transform_err; + + if (st->state==SITE_STOP) { + BUF_FREE(buf); + return; + } + + /* In all other states we consider delivering the packet if we have + a valid key and a valid address to send it to. */ + if (st->current_valid && st->peer_valid) { + /* Transform it and send it */ + *(uint32_t *)buf_prepend(buf,4)=LABEL_MSG9; + st->current_transform->forwards(st->current_transform->st, + buf, &transform_err); + *(uint32_t *)buf_prepend(buf,4)=LABEL_MSG0; + *(uint32_t *)buf_prepend(buf,4)=(uint32_t)st; + *(uint32_t *)buf_prepend(buf,4)=st->remote_session_id; + st->comm->sendmsg(st->comm->st,buf,&st->peer); + BUF_FREE(buf); + return; + } + + if (st->state==SITE_RUN) { + BUF_FREE(buf); /* We throw the outgoing packet away */ + slog(st,LOG_SETUP_INIT,"initiating key exchange"); + enter_state_resolve(st); + return; + } + + /* Otherwise we're in the middle of key setup or a wait - just + throw the outgoing packet away */ + slog(st,LOG_DROP,"discarding outgoing packet"); + BUF_FREE(buf); + return; +} + +/* This function is called by the communication device to deliver + packets from our peers. */ +static bool_t site_incoming(void *sst, struct buffer_if *buf, + struct sockaddr_in *source) +{ + struct site *st=sst; + uint32_t dest=*(uint32_t *)buf->start; + + if (dest==0) { + if (buf->size<(st->setupsiglen+8+NONCELEN)) return False; + /* It could be for any site - it should have LABEL_MSG1 and + might have our name and our peer's name in it */ + if (memcmp(buf->start+8,st->setupsig,st->setupsiglen)==0) { + dump_packet(st,buf,source,True); + /* It's addressed to us. Decide what to do about it. */ + if (st->state==SITE_RUN || st->state==SITE_RESOLVE || + st->state==SITE_WAIT) { + /* We should definitely process it */ + if (process_msg1(st,buf,source)) { + slog(st,LOG_SETUP_INIT,"key setup initiated by peer"); + enter_state_sentmsg2(st); + } else { + slog(st,LOG_ERROR,"failed to process incoming msg1"); + } + BUF_FREE(buf); + return True; + } + if (st->state==SITE_SENTMSG1) { + /* We've just sent a message 1! They may have crossed on + the wire. If we have priority then we ignore the + incoming one, otherwise we process it as usual. */ + if (st->setup_priority) { + BUF_FREE(buf); + slog(st,LOG_DUMP,"crossed msg1s; we are higher " + "priority => ignore incoming msg1"); + return True; + } else { + slog(st,LOG_DUMP,"crossed msg1s; we are lower " + "priority => use incoming msg1"); + if (process_msg1(st,buf,source)) { + BUF_FREE(&st->buffer); /* Free our old message 1 */ + enter_state_sentmsg2(st); + } else { + slog(st,LOG_ERROR,"failed to process an incoming " + "crossed msg1 (we have low priority)"); + } + BUF_FREE(buf); + return True; + } + } + /* The message 1 was received at an unexpected stage of the + key setup. XXX POLICY - what do we do? */ + slog(st,LOG_UNEXPECTED,"unexpected incoming message 1"); + BUF_FREE(buf); + return True; + } + return False; /* Not for us. */ + } + if (dest==(uint32_t)st) { + uint32_t msgtype=*(uint32_t *)(buf->start+8); + /* Explicitly addressed to us */ + if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True); + switch (msgtype) { + case LABEL_MSG0: + process_msg0(st,buf,source); + break; + case LABEL_MSG1: + /* Setup packet: should not have been explicitly addressed + to us */ + slog(st,LOG_SECURITY,"incoming explicitly addressed msg1"); + break; + case LABEL_MSG2: + /* Setup packet: expected only in state SENTMSG1 */ + if (st->state!=SITE_SENTMSG1) { + slog(st,LOG_UNEXPECTED,"unexpected MSG2"); + } else if (process_msg2(st,buf,source)) + enter_state_sentmsg3(st); + else { + slog(st,LOG_SECURITY,"invalid MSG2"); + } + break; + case LABEL_MSG3: + /* Setup packet: expected only in state SENTMSG2 */ + if (st->state!=SITE_SENTMSG2) { + slog(st,LOG_UNEXPECTED,"unexpected MSG3"); + } else if (process_msg3(st,buf,source)) + enter_state_sentmsg4(st); + else { + slog(st,LOG_SECURITY,"invalid MSG3"); + } + break; + case LABEL_MSG4: + /* Setup packet: expected only in state SENTMSG3 */ + if (st->state!=SITE_SENTMSG3) { + slog(st,LOG_UNEXPECTED,"unexpected MSG4"); + } else if (process_msg4(st,buf,source)) + enter_state_sentmsg5(st); + else { + slog(st,LOG_SECURITY,"invalid MSG4"); + } + break; + case LABEL_MSG5: + /* Setup packet: expected only in state SENTMSG4 or RUN */ + if (st->state!=SITE_SENTMSG4 && st->state!=SITE_RUN) { + slog(st,LOG_UNEXPECTED,"unexpected MSG5"); + } else if (process_msg5(st,buf,source)) { + send_msg6(st); + } else { + slog(st,LOG_SECURITY,"invalid MSG5"); + } + break; + case LABEL_MSG6: + /* Setup packet: expected only in state SENTMSG5 */ + if (st->state!=SITE_SENTMSG5) { + slog(st,LOG_UNEXPECTED,"unexpected MSG6"); + } else if (process_msg6(st,buf,source)) { + BUF_FREE(&st->buffer); /* Free message 5 */ + activate_new_key(st); + } else { + slog(st,LOG_SECURITY,"invalid MSG6"); + } + break; + case LABEL_MSG8: + /* NAK packet: enter state where we ping and check for response */ + slog(st,LOG_ERROR,"received a NAK"); + break; + default: + slog(st,LOG_SECURITY,"received message of unknown type 0x%08x", + msgtype); + break; + } + BUF_FREE(buf); + return True; + } + + return False; +} + +static void site_control(void *vst, bool_t run) +{ + struct site *st=vst; + if (run) enter_state_run(st); + else enter_state_stop(st); +} + +static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct site *st; + item_t *item; + dict_t *dict; + + st=safe_malloc(sizeof(*st),"site_apply"); + + st->cl.description="site"; + st->cl.type=CL_SITE; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.control=site_control; + st->ops.status=site_status; + + /* First parameter must be a dict */ + item=list_elem(args,0); + if (!item || item->type!=t_dict) + cfgfatal(loc,"site","parameter must be a dictionary\n"); + + dict=item->data.dict; + st->netlink=find_cl_if(dict,"netlink",CL_NETLINK,True,"site",loc); + st->comm=find_cl_if(dict,"comm",CL_COMM,True,"site",loc); + st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc); + st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc); + st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc); + + st->localname=dict_read_string(dict, "local-name", True, "site", loc); + st->privkey=find_cl_if(dict,"local-key",CL_RSAPRIVKEY,True,"site",loc); + st->remoteport=dict_read_number(dict,"port",True,"site",loc,0); + + st->remotename=dict_read_string(dict, "name", True, "site", loc); + st->address=dict_read_string(dict, "address", False, "site", loc); + dict_read_subnet_list(dict, "networks", True, "site", loc, + &st->remotenets); + st->pubkey=find_cl_if(dict,"key",CL_RSAPUBKEY,True,"site",loc); + + st->transform= + find_cl_if(dict,"transform",CL_TRANSFORM,True,"site",loc); + + st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc); + st->hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc); + + st->key_lifetime=dict_read_number(dict,"key-lifetime", + False,"site",loc,DEFAULT_KEY_LIFETIME); + st->setup_retries=dict_read_number(dict,"setup-retries", + False,"site",loc,DEFAULT_SETUP_RETRIES); + st->setup_timeout=dict_read_number(dict,"setup-timeout", + False,"site",loc,DEFAULT_SETUP_TIMEOUT); + st->wait_timeout=dict_read_number(dict,"wait-time", + False,"site",loc,DEFAULT_WAIT_TIME); + /* XXX should be configurable */ + st->log_events=LOG_SECURITY|LOG_ERROR| + LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SETUP_INIT|LOG_SETUP_TIMEOUT; + + /* The information we expect to see in incoming messages of type 1 */ + st->setupsiglen=strlen(st->remotename)+strlen(st->localname)+8; + st->setupsig=safe_malloc(st->setupsiglen,"site_apply"); + *(uint32_t *)&(st->setupsig[0])=LABEL_MSG1; + *(uint16_t *)&(st->setupsig[4])=htons(strlen(st->remotename)); + memcpy(&st->setupsig[6],st->remotename,strlen(st->remotename)); + *(uint16_t *)&(st->setupsig[6+strlen(st->remotename)])= + htons(strlen(st->localname)); + memcpy(&st->setupsig[8+strlen(st->remotename)],st->localname, + strlen(st->localname)); + st->setup_priority=(strcmp(st->localname,st->remotename)>0); + + buffer_new(&st->buffer,SETUP_BUFFER_LEN); + + /* We are interested in poll(), but only for timeouts. We don't have + any fds of our own. */ + register_for_poll(st, site_beforepoll, site_afterpoll, 0, "site"); + st->timeout=0; + + st->current_valid=False; + st->current_key_timeout=0; + st->peer_valid=False; + /* XXX mlock these */ + st->dhsecret=safe_malloc(st->dh->len,"site:dhsecret"); + st->sharedsecret=safe_malloc(st->transform->keylen,"site:sharedsecret"); + + /* We need to register the remote networks with the netlink device */ + st->netlink_cid=st->netlink->regnets(st->netlink->st, &st->remotenets, + site_outgoing, st, + st->transform->max_start_pad+(4*4), + st->transform->max_end_pad); + + st->comm->request_notify(st->comm->st, st, site_incoming); + + st->current_transform=st->transform->create(st->transform->st); + st->new_transform=st->transform->create(st->transform->st); + + enter_state_stop(st); + + return new_closure(&st->cl); +} + +init_module site_module; +void site_module(dict_t *dict) +{ + add_closure(dict,"site",site_apply); +} diff --git a/testconfig b/testconfig new file mode 100644 index 0000000..4541199 --- /dev/null +++ b/testconfig @@ -0,0 +1,158 @@ +# secnet configuration file + +# This file defines a dictionary full of configuration information for +# secnet. Two keys must be defined in this file for secnet to +# start. One is "system", a dictionary containing systemwide control +# parameters. The other is "sites", a list of all the sites that you +# intend to communicate with. + +# Other files can be included inline by writing "include filename" at +# the start of a line. + +# The configuration file has a fairly simple syntax: +# key definition; or key = definition; (the "=" is optional) +# ...sets 'key' in the current dictionary to 'definition'. +# +# "key" is [[:alpha:]_][[:alnum:]\-_]* +# +# definition may be one of the following: +# a string, in quotes +# a number, in decimal +# a dictionary, in { } +# a path to a key that already exists, to reference that definition +# a "closure", followed by arguments +# +# paths are key1/key2/key3... (starting from wherever we find key1, i.e. in +# the current dictionary or any of its parents) +# alternatively /key1/key2/key3... (to start from the root) +# +# closures are followed by an argument list in ( ), and may return +# whatever type they like (including other closures) +# +# closure { definitions } is short for closure({definitions}). +# +# Whenever secnet looks for a key it checks the (lexical) parent dictionaries +# as well until it finds it or reaches the root. This is useful for setting +# defaults for large collections of dictionaries (eg. defining sites). +# +# It is also permissible to list other dictionaries before a dictionary +# definition, eg. {definitions}. These will be +# searched in order for keys, before the lexical parent. (Not yet implemented) +# +# secnet predefines some keys in the root dictionary; some useful ones are: +# yes, true, True, TRUE: the boolean value True +# no, false, False, FALSE: the boolean value False +# makelist: turns a dictionary (arg1) into a list (return value) +# readfile: reads a file (arg1) and returns it as a string +# +# secnet modules also predefine keys, eg. "adns", "randomfile", etc. +# See the module documentation for more information. + +# After the configuration file is read, secnet looks for particular keys +# in configuration space to tell it what to do: +# system: system-wide parameters (control, logging, etc.) +# sites: a list of sites with which to communicate + +# Log facility +log logfile("secnet","local2"); # Not yet implemented, goes to stderr + +# Systemwide configuration (all other configuration is per-site): +# log a log facility for program messages +# userid who we try to run as after setup +# pidfile +system { +# userid "steve"; +# pidfile "/var/run/secnet.pid"; + pidfile "foo.pid"; +}; + +# Parameters for each remote site (arguments to the site() closure): +# things we configure locally +# buffer buffer for constructing/sending/receiving packets +# netlink user/kernel netlink device for this tunnel +# comm UDP communication +# resolver resolver to use for name lookups +# log a log destination for this connection +# log-events string list: which events we log +# random a source of randomness + +# our local configuration visible to the outside world +# local-name string: how we identify ourselves to them +# local-key our own private RSA key +# local-port port number we listen on + +# their configuration visible to us +# name string: how they identify themselves +# address string: use with resolver to find their IP address +# networks string list: their networks for us +# key the remote site's RSA public key +# port port we send to to contact remote site + +# things both ends must agree on +# transform routine for bulk encryption +# dh Diffie-Hellman parameters +# hash secure hash function + +# things both ends ought to agree on, but don't have to +# key-lifetime max session key lifetime, in milliseconds +# setup-retries max retransmits of a key setup packet +# setup-timeout wait between retransmits of key setup packets, in ms +# wait-time wait between unsuccessful key setup attempts, in ms + +netlink userv-ipif { + name "userv-ipif"; # Printed in log messages from this netlink + # userv-path "/usr/bin/userv"; + # service-user "root"; + # service-name "ipif"; + + # local networks served by this netlink device + # incoming tunneled packets for other networks will be discarded + networks "192.168.73.0/24", "192.168.1.0/24", "172.19.71.0/24"; + local-address "192.168.73.72"; # IP address of interface + secnet-address "192.168.73.73"; # IP address of secnet + mtu 1400; + + buffer sysbuffer(); # userv/ipif needs a buffer to build incoming + # packets from the netlink device before passing them + # to the site layer +}; +comm udp { + port 1234; + buffer sysbuffer(4096,{lockdown=yes;}); +}; +resolver adns { + config="wibble wobble"; +}; +# log is defined earlier - we share it with the system +log-events "init","up","down"; +random randomfile("/dev/urandom",no); + +local-name "myrddin"; +local-key rsa-private("private-key"); + +transform serpent256-cbc { + max-sequence-skew 10; +}; + +dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2"); +hash md5; + +key-lifetime 20000; + +zealot { + name "zealot"; + address "zealot.sinister.greenend.org.uk"; + port 5678; + networks "192.168.73.74/32", "192.168.73.75/32"; + key rsa-public("35","131453873229748492184986747327990913828179255774895541667982108408897406369168730551214152673574619385573519088922707364993860644376262000057302119569116289693520981276177337391324943049983046703853106890057346878967444626093102422836819979338760420960495059950787838142162794317002315919126174831103379472833"); + }; + +myrddin { + name "myrddin"; + address "myrddin.sinister.greenend.org.uk"; + port 1234; + networks "192.168.73.72/32", "192.168.73.73/32"; + key rsa-public("35","154107175724781677184264293617887954015562225725852111745852699493257053099810379926047345975839848434403852210573185384327420788855664167034282567346429150999373740871227795773749618022407366186555483566435251279808390618987056868368084933125373643004284007109877210578088697520329039753099981203724057693543"); + }; + +sites site(zealot); diff --git a/testconfigz b/testconfigz new file mode 100644 index 0000000..f2a66a6 --- /dev/null +++ b/testconfigz @@ -0,0 +1,153 @@ +# secnet configuration file + +# This file defines a dictionary full of configuration information for +# secnet. Two keys must be defined in this file for secnet to +# start. One is "system", a dictionary containing systemwide control +# parameters. The other is "sites", a list of all the sites that you +# intend to communicate with. + +# Other files can be included inline by writing "include filename" at +# the start of a line. + +# The configuration file has a fairly simple syntax: +# key definition; or key = definition; (the "=" is optional) +# ...sets 'key' in the current dictionary to 'definition'. +# +# "key" is [[:alpha:]_][[:alnum:]\-_]* +# +# definition may be one of the following: +# a string, in quotes +# a number, in decimal +# a dictionary, in { } +# a path to a key that already exists, to reference that definition +# a "closure", followed by arguments +# +# paths are key1/key2/key3... (starting from wherever we find key1, i.e. in +# the current dictionary or any of its parents) +# alternatively /key1/key2/key3... (to start from the root) +# +# closures are followed by an argument list in ( ), and may return +# whatever type they like (including other closures) +# +# closure { definitions } is short for closure({definitions}). +# +# Whenever secnet looks for a key it checks the (lexical) parent dictionaries +# as well until it finds it or reaches the root. This is useful for setting +# defaults for large collections of dictionaries (eg. defining sites). +# +# It is also permissible to list other dictionaries before a dictionary +# definition, eg. {definitions}. These will be +# searched in order for keys, before the lexical parent. (Not yet implemented) +# +# secnet predefines some keys in the root dictionary; some useful ones are: +# yes, true, True, TRUE: the boolean value True +# no, false, False, FALSE: the boolean value False +# makelist: turns a dictionary (arg1) into a list (return value) +# readfile: reads a file (arg1) and returns it as a string +# +# secnet modules also predefine keys, eg. "adns", "randomfile", etc. +# See the module documentation for more information. + +# After the configuration file is read, secnet looks for particular keys +# in configuration space to tell it what to do: +# system: system-wide parameters (control, logging, etc.) +# sites: a list of sites with which to communicate + +# Log facility +log logfile("secnet","local2"); # Not yet implemented, goes to stderr + +# Systemwide configuration (all other configuration is per-site): +# log a log facility for program messages +# userid who we try to run as after setup +# pidfile +system { +# userid "tunnel"; +# pidfile "/var/run/secnet.pid"; +}; + +# Parameters for each remote site (arguments to the site() closure): +# things we configure locally +# buffer buffer for constructing/sending/receiving packets +# netlink user/kernel netlink device for this tunnel +# comm UDP communication +# resolver resolver to use for name lookups +# log a log destination for this connection +# log-events string list: which events we log +# random a source of randomness + +# our local configuration visible to the outside world +# local-name string: how we identify ourselves to them +# local-key our own private RSA key +# local-port port number we listen on + +# their configuration visible to us +# name string: how they identify themselves +# address string: use with resolver to find their IP address +# networks string list: their networks for us +# key the remote site's RSA public key +# port port we send to to contact remote site + +# things both ends must agree on +# transform routine for bulk encryption +# dh Diffie-Hellman parameters +# hash secure hash function + +# A buffer for all sites to share, to construct outgoing packets +buffer sysbuffer(4096,{lockdown=yes;}); + +netlink userv-ipif { + name "userv-ipif"; # Printed in log messages from this netlink + # userv-path "/usr/bin/userv"; + # service-user "root"; + # service-name "ipif"; + + # local networks served by this netlink device + # incoming tunneled packets for other networks will be discarded + networks "192.168.73.74/32"; + local-address "192.168.73.74"; # IP address of interface + secnet-address "192.168.73.75"; # IP address of secnet + mtu 1400; + + buffer sysbuffer(); # userv/ipif needs a buffer to build incoming + # packets from the netlink device before passing them + # to the site layer +}; +comm udp { + port 5678; + # buffer shared with sites +}; +resolver adns { + noenv=yes; # yes is a name for the boolean "true" + nameservers "127.0.0.1","192.168.73.4"; +}; +# log is defined earlier - we share it with the system +log-events "init","up","down"; +random randomfile("/dev/urandom",no); + +local-name "zealot"; +local-key rsa-private("private-key"); + +transform serpent256-cbc { + max-sequence-skew 10; +}; + +dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2"); +hash md5; + +zealot { + name "zealot"; + address "zealot.sinister.greenend.org.uk"; + port 5678; + networks "192.168.73.74/32", "192.168.73.75/32"; + key rsa-public("35","131453873229748492184986747327990913828179255774895541667982108408897406369168730551214152673574619385573519088922707364993860644376262000057302119569116289693520981276177337391324943049983046703853106890057346878967444626093102422836819979338760420960495059950787838142162794317002315919126174831103379472833"); + }; + +myrddin { + name "myrddin"; + address "myrddin.sinister.greenend.org.uk"; + port 1234; + networks "192.168.73.72/32", "192.168.73.73/32"; + key rsa-public("35","154107175724781677184264293617887954015562225725852111745852699493257053099810379926047345975839848434403852210573185384327420788855664167034282567346429150999373740871227795773749618022407366186555483566435251279808390618987056868368084933125373643004284007109877210578088697520329039753099981203724057693543"); + }; + +sites site(myrddin); diff --git a/testsites b/testsites new file mode 100644 index 0000000..e3ca05f --- /dev/null +++ b/testsites @@ -0,0 +1,27 @@ +# This is secnet's equivalent of the 'vpn-sites' file + +# Global parameters for all sites (can be overridden if some sites want to +# use other parameters) + +sinister-vpn { + +dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2"); +hash md5; + +# All the remote sites we might want to talk to +groad { + name "gilbert-road"; + address "relativity.dynamic.greenend.org.uk"; + port 5678; + networks "172.18.45.0/24"; + key rsa-public("35","153279875126380522437827076871354104097683702803616313419670959273217685015951590424876274370401136371563604396779864283483623325238228723798087715987495590765759771552692972297669972616769731553560605291312242789575053620182470998166393580503400960149506261455420521811814445675652857085993458063584337404329"); + }; +chiark { + name "chiark"; + address "chiark.greenend.org.uk"; + port 2345; + networks "172.31.80.8/32"; + key rsa-public("35","127129251486595848418110457412760173306108766728826928401101049305981756206590497728003410607079784801800518655333869748411389535602962935662455800359479854817411356280256727700339726067467542581881498715223842814199845818940876020358790270431574802728927549159072714772035370848962882690573372309719699356913"); + }; + +}; diff --git a/transform.c b/transform.c new file mode 100644 index 0000000..f94f5ae --- /dev/null +++ b/transform.c @@ -0,0 +1,342 @@ +/* Transform module - bulk data transformation */ + +/* For now it's hard-coded to do sequence + number/pkcs5/serpent-cbcmac/serpent with a 256 bit key for each + instance of serpent. We also require key material for the IVs for + cbcmac and cbc. Hack: we're not using full 128-bit IVs, we're just + using 32 bits and encrypting to get the full IV to save space in + the packets sent over the wire. */ + +#include +#include "secnet.h" +#include "util.h" +#include "serpent.h" + +/* Required key length in bytes */ +#define REQUIRED_KEYLEN ((512+64+32)/8) + +struct transform { + closure_t cl; + uint32_t line; + struct transform_if ops; + uint32_t max_seq_skew; +}; + +struct transform_inst { + struct transform_inst_if ops; + struct keyInstance cryptkey; + struct keyInstance mackey; + uint32_t cryptiv; + uint32_t maciv; + uint32_t sendseq; + uint32_t lastrecvseq; + uint32_t max_skew; + bool_t keyed; +}; + +#define PKCS5_MASK 15 + +static bool_t transform_setkey(void *sst, uint8_t *key, uint32_t keylen) +{ + struct transform_inst *ti=sst; + + if (keylencryptkey,256,key); + serpent_makekey(&ti->mackey,256,key+32); + ti->cryptiv=*(uint32_t *)(key+64); + ti->maciv=*(uint32_t *)(key+68); + ti->sendseq=*(uint32_t *)(key+72); + ti->lastrecvseq=ti->sendseq; + ti->keyed=True; + + return True; +} + +static void transform_delkey(void *sst) +{ + struct transform_inst *ti=sst; + + memset(&ti->cryptkey,0,sizeof(ti->cryptkey)); + memset(&ti->mackey,0,sizeof(ti->mackey)); + ti->keyed=False; +} + +static uint32_t transform_forward(void *sst, struct buffer_if *buf, + char **errmsg) +{ + struct transform_inst *ti=sst; + uint8_t *padp; + int padlen; + uint32_t iv[4]; + uint32_t macplain[4]; + uint32_t macacc[4]; + uint32_t *n, *p; + + if (!ti->keyed) { + *errmsg="transform unkeyed"; + return 1; + } + + /* Sequence number */ + *(uint32_t *)buf_prepend(buf,4)=htonl(ti->sendseq); + ti->sendseq++; + + /* PKCS5, stolen from IWJ */ + /* eg with blocksize=4 mask=3 mask+2=5 */ + /* msgsize 20 21 22 23 24 */ + padlen= PKCS5_MASK-buf->size; /* -17 -18 -19 -16 -17 */ + padlen &= PKCS5_MASK; /* 3 2 1 0 3 */ + padlen++; /* 4 3 2 1 4 */ + + padp=buf_append(buf,padlen); + memset(padp,padlen,padlen); + + /* Serpent-CBCMAC. We expand the IV from 32-bit to 128-bit using + one encryption. Then we do the MAC and append the result. We don't + bother sending the IV - it's the same each time. (If we wanted to send + it we've have to add 16 bytes to each message, not 4, so that the + message stays a multiple of 16 bytes long.) */ + memset(iv,0,16); + iv[0]=ti->maciv; + serpent_encrypt(&ti->mackey,iv,macacc); + + /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted + block encrypted once again. */ + for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + { + macplain[0]=macacc[0]^n[0]; + macplain[1]=macacc[1]^n[1]; + macplain[2]=macacc[2]^n[2]; + macplain[3]=macacc[3]^n[3]; + serpent_encrypt(&ti->mackey,macplain,macacc); + } + serpent_encrypt(&ti->mackey,macacc,macacc); + memcpy(buf_append(buf,16),macacc,16); + + /* Serpent-CBC. We expand the ID as for CBCMAC, do the encryption, + and prepend the IV before increasing it. */ + memset(iv,0,16); + iv[0]=ti->cryptiv; + serpent_encrypt(&ti->cryptkey,iv,iv); + + /* CBC: each block is XORed with the previous encrypted block (or the IV) + before being encrypted. */ + p=iv; + for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + { + n[0]=p[0]^n[0]; + n[1]=p[1]^n[1]; + n[2]=p[2]^n[2]; + n[3]=p[3]^n[3]; + serpent_encrypt(&ti->cryptkey,n,n); + p=n; + } + + *(uint32_t *)buf_prepend(buf,4)=ti->cryptiv; + ti->cryptiv++; + + return 0; +} + +static uint32_t transform_reverse(void *sst, struct buffer_if *buf, + char **errmsg) +{ + struct transform_inst *ti=sst; + uint8_t *padp; + unsigned padlen; + int i; + uint32_t seqnum, skew; + uint32_t iv[4]; + uint32_t pct[4]; + uint32_t macplain[4]; + uint32_t macacc[4]; + uint32_t *n; + uint32_t *macexpected; + + if (!ti->keyed) { + *errmsg="transform unkeyed"; + return 1; + } + + /* CBC */ + memset(iv,0,16); + iv[0]=*(uint32_t *)buf_unprepend(buf,4); + serpent_encrypt(&ti->cryptkey,iv,iv); + /* XXX assert bufsize is multiple of blocksize */ + for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + { + pct[0]=n[0]; pct[1]=n[1]; pct[2]=n[2]; pct[3]=n[3]; + serpent_decrypt(&ti->cryptkey,n,n); + n[0]=iv[0]^n[0]; + n[1]=iv[1]^n[1]; + n[2]=iv[2]^n[2]; + n[3]=iv[3]^n[3]; + iv[0]=pct[0]; iv[1]=pct[1]; iv[2]=pct[2]; iv[3]=pct[3]; + } + + /* CBCMAC */ + macexpected=buf_unappend(buf,16); + memset(iv,0,16); + iv[0]=ti->maciv; + serpent_encrypt(&ti->mackey,iv,macacc); + + /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted + block encrypted once again. */ + for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + { + macplain[0]=macacc[0]^n[0]; + macplain[1]=macacc[1]^n[1]; + macplain[2]=macacc[2]^n[2]; + macplain[3]=macacc[3]^n[3]; + serpent_encrypt(&ti->mackey,macplain,macacc); + } + serpent_encrypt(&ti->mackey,macacc,macacc); + if (memcmp(macexpected,macacc,16)!=0) { + *errmsg="invalid MAC"; + return 1; + } + + /* PKCS5, stolen from IWJ */ + + padp=buf_unappend(buf,1); + padlen=*padp; + if (!padlen || (padlen > PKCS5_MASK+1)) { + *errmsg="pkcs5: invalid length"; + return 1; + } + + padp=buf_unappend(buf,padlen-1); + for (i=0; ilastrecvseq; + if (skew<10) { + /* Ok */ + ti->lastrecvseq=seqnum; + } else if ((0-skew)<10) { + /* Ok */ + } else { + /* Too much skew */ + *errmsg="seqnum: too much skew"; + return 1; + } + + return 0; +} + +static void transform_destroy(void *sst) +{ + struct transform_inst *st=sst; + + memset(st,0,sizeof(*st)); /* Destroy key material */ + free(st); +} + +static struct transform_inst_if *transform_create(void *sst) +{ + struct transform_inst *ti; + struct transform *st=sst; + + ti=safe_malloc(sizeof(*ti),"transform_create"); + /* mlock XXX */ + + ti->ops.st=ti; + ti->ops.setkey=transform_setkey; + ti->ops.delkey=transform_delkey; + ti->ops.forwards=transform_forward; + ti->ops.reverse=transform_reverse; + ti->ops.destroy=transform_destroy; + ti->max_skew=st->max_seq_skew; + ti->keyed=False; + + return &ti->ops; +} + +static list_t *transform_apply(closure_t *self, struct cloc loc, + dict_t *context, list_t *args) +{ + struct transform *st; + item_t *item; + dict_t *dict; + + st=safe_malloc(sizeof(*st),"serpent"); + st->cl.description="serpent-cbc256"; + st->cl.type=CL_TRANSFORM; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.max_start_pad=28; /* 4byte seqnum, 16byte pad, 4byte MACIV, + 4byte IV */ + st->ops.max_end_pad=16; /* 16byte CBCMAC */ + + /* We need 256*2 bits for serpent keys, 32 bits for CBC-IV and 32 bits + for CBCMAC-IV, and 32 bits for init sequence number */ + st->ops.keylen=REQUIRED_KEYLEN; + st->ops.create=transform_create; + + /* First parameter must be a dict */ + item=list_elem(args,0); + if (!item || item->type!=t_dict) + cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n"); + + dict=item->data.dict; + st->max_seq_skew=dict_read_number(dict, "max-sequence-skew", + False, "serpent-cbc256", loc, 10); + + return new_closure(&st->cl); +} + +init_module transform_module; +void transform_module(dict_t *dict) +{ + struct keyInstance k; + uint8_t data[32]; + uint32_t plaintext[4]; + uint32_t ciphertext[4]; + + /* Serpent self-test */ + memset(data,0,32); + serpent_makekey(&k,256,data); + plaintext[0]=0x00000000; + plaintext[1]=0x00000001; + plaintext[2]=0x00000002; + plaintext[3]=0x00000003; + serpent_encrypt(&k,plaintext,ciphertext); + if (ciphertext[3]!=0x7ca73bb0 || + ciphertext[2]!=0x83C31E69 || + ciphertext[1]!=0xec52bd82 || + ciphertext[0]!=0x27a46120) { + fatal("transform_module: serpent failed self-test (encrypt)\n"); + } + serpent_decrypt(&k,ciphertext,plaintext); + if (plaintext[0]!=0 || + plaintext[1]!=1 || + plaintext[2]!=2 || + plaintext[3]!=3) { + fatal("transform_module: serpent failed self-test (decrypt)\n"); + } + + add_closure(dict,"serpent256-cbc",transform_apply); +} diff --git a/udp.c b/udp.c new file mode 100644 index 0000000..177ce1c --- /dev/null +++ b/udp.c @@ -0,0 +1,211 @@ +/* UDP send/receive module for secnet */ + +/* This module enables sites to communicate by sending UDP + * packets. When an instance of the module is created we can + * optionally bind to a particular local IP address (not implemented + * yet). + * + * Sites register an interest in local port numbers for receiving + * packets, and can also send packets. We don't care about the source + * port number for sending packets. + * + * Packets are offered to registered receivers in turn. Once one + * accepts it, it isn't offered to any more. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "secnet.h" +#include "util.h" + +static beforepoll_fn udp_beforepoll; +static afterpoll_fn udp_afterpoll; +static comm_request_notify_fn request_notify; +static comm_release_notify_fn release_notify; +static comm_sendmsg_fn udp_sendmsg; + +/* The UDP module exports a pure closure which can be used to construct a + * UDP send/receive module. Arguments: + */ + +struct notify_list { + comm_notify_fn *fn; + void *state; + struct notify_list *next; +}; + +struct udp { + closure_t cl; + struct comm_if ops; + struct cloc loc; + int fd; + struct buffer_if *rbuf; + struct notify_list *notify; +}; + +static int udp_beforepoll(void *state, struct pollfd *fds, int *nfds_io, + int *timeout_io, const struct timeval *tv, + uint64_t *now) +{ + struct udp *st=state; + if (*nfds_io<1) { + *nfds_io=1; + return ERANGE; + } + *nfds_io=1; + fds->fd=st->fd; + fds->events=POLLIN; + return 0; +} + +static void udp_afterpoll(void *state, struct pollfd *fds, int nfds, + const struct timeval *tv, uint64_t *now) +{ + struct udp *st=state; + struct sockaddr_in from; + int fromlen; + struct notify_list *n; + bool_t done; + int rv; + + if (nfds && (fds->revents & POLLIN)) { + do { + fromlen=sizeof(from); + BUF_ASSERT_FREE(st->rbuf); + BUF_ALLOC(st->rbuf,"udp_afterpoll"); + rv=recvfrom(st->fd, st->rbuf->start, st->rbuf->len, 0, + (struct sockaddr *)&from, &fromlen); + if (rv>0) { + st->rbuf->size=rv; + done=False; + for (n=st->notify; n; n=n->next) { + if (n->fn(n->state, st->rbuf, &from)) { + done=True; + break; + } + } + if (!done) { + /* XXX manufacture and send NAK packet */ + Message(M_WARNING,"Need to send NAK\n"); + BUF_FREE(st->rbuf); + } + BUF_ASSERT_FREE(st->rbuf); + } else { + BUF_FREE(st->rbuf); + } + } while (rv>=0); + } +} + +static void request_notify(void *commst, void *nst, comm_notify_fn *fn) +{ + struct udp *st=commst; + struct notify_list *n; + + n=safe_malloc(sizeof(*n),"request_notify"); + n->fn=fn; + n->state=nst; + n->next=st->notify; + st->notify=n; +} + +static void release_notify(void *commst, void *nst, comm_notify_fn *fn) +{ + struct udp *st=commst; + struct notify_list *n, **p, *t; + + /* XXX untested */ + p=&st->notify; + for (n=st->notify; n; ) + { + if (n->state==nst && n->fn==fn) { + t=n; + *p=n->next; + n=n->next; + free(t); + } else { + p=&n->next; + n=n->next; + } + } +} + +static bool_t udp_sendmsg(void *commst, struct buffer_if *buf, + struct sockaddr_in *dest) +{ + struct udp *st=commst; + + /* XXX fix error reporting */ + sendto(st->fd, buf->start, buf->size, 0, + (struct sockaddr *)dest, sizeof(*dest)); + + return True; +} + +static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct udp *st; + item_t *i; + dict_t *d; + uint16_t local_port=0; + struct sockaddr_in addr; + + st=safe_malloc(sizeof(*st),"udp_apply(st)"); + st->loc=loc; + st->cl.description="udp"; + st->cl.type=CL_COMM; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.st=st; + st->ops.request_notify=request_notify; + st->ops.release_notify=release_notify; + st->ops.sendmsg=udp_sendmsg; + + i=list_elem(args,0); + if (!i || i->type!=t_dict) { + cfgfatal(st->loc,"udp","first argument must be a dictionary\n"); + } + d=i->data.dict; + + local_port=dict_read_number(d,"port",False,"udp",st->loc,0); + st->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,"udp",st->loc); + + st->fd=socket(AF_INET, SOCK_DGRAM, 0); + if (st->fd<0) { + fatal_perror("udp_apply (%s:%d): socket",loc.file,loc.line); + } + if (fcntl(st->fd, F_SETFL, fcntl(st->fd, F_GETFL)|O_NONBLOCK)==-1) { + fatal_perror("udp_apply (%s:%d): fcntl(set O_NONBLOCK)", + loc.file,loc.line); + } + if (fcntl(st->fd, F_SETFD, FD_CLOEXEC)==-1) { + fatal_perror("udp_apply (%s:%d): fcntl(set FD_CLOEXEC)", + loc.file,loc.line); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family=AF_INET; + if (local_port) { + addr.sin_port=htons(local_port); + } + if (bind(st->fd, (struct sockaddr *)&addr, sizeof(addr))!=0) { + fatal_perror("udp_apply (%s:%d): bind",loc.file,loc.line); + } + + register_for_poll(st,udp_beforepoll,udp_afterpoll,1,"udp"); + + return new_closure(&st->cl); +} + +init_module udp_module; +void udp_module(dict_t *dict) +{ + add_closure(dict,"udp",udp_apply); +} diff --git a/util.c b/util.c new file mode 100644 index 0000000..7e5a39a --- /dev/null +++ b/util.c @@ -0,0 +1,572 @@ +/* $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 + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "secnet.h" + +#define MIN_BUFFER_SIZE 64 +#define DEFAULT_BUFFER_SIZE 4096 +#define MAX_BUFFER_SIZE 131072 + +static char *hexdigits="0123456789abcdef"; + +uint32_t message_level=M_WARNING|M_ERROR|M_FATAL; +uint32_t syslog_level=M_WARNING|M_ERROR|M_FATAL; +static uint32_t current_phase=0; + +struct phase_hook { + hook_fn *fn; + void *state; + struct phase_hook *next; +}; + +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; + } + 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); +} + +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); +} + +char *safe_strdup(char *s, char *message) +{ + char *d; + d=strdup(s); + if (!d) { + fatal_perror(message); + } + return d; +} + +void *safe_malloc(size_t size, char *message) +{ + void *r; + r=malloc(size); + if (!r) { + fatal_perror(message); + } + return r; +} + +/* Convert a buffer into its MP_INT representation */ +void read_mpbin(MP_INT *a, uint8_t *bin, int binsize) +{ + char *buff; + int i; + + buff=safe_malloc(binsize*2 + 1,"read_mpbin"); + + for (i=0; i> 4]; + buff[i*2+1]=hexdigits[(bin[i] & 0xf)]; + } + buff[binsize*2]=0; + + mpz_set_str(a, buff, 16); + free(buff); +} + +/* Convert a MP_INT into a hex string */ +char *write_mpstring(MP_INT *a) +{ + char *buff; + + buff=safe_malloc(mpz_sizeinbase(a,16)+2,"write_mpstring"); + mpz_get_str(buff, 16, a); + return buff; +} + +static uint8_t hexval(uint8_t c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': return 10; + case 'A': return 10; + case 'b': return 11; + case 'B': return 11; + case 'c': return 12; + case 'C': return 12; + case 'd': return 13; + case 'D': return 13; + case 'e': return 14; + case 'E': return 14; + case 'f': return 15; + case 'F': return 15; + } + return -1; +} + +/* Convert a MP_INT into a buffer; return length; truncate if necessary */ +uint32_t write_mpbin(MP_INT *a, uint8_t *buffer, uint32_t buflen) +{ + char *hb; + int i,j,l; + + if (buflen==0) return 0; + hb=write_mpstring(a); + + l=strlen(hb); + i=0; j=0; + if (l&1) { + /* The number starts with a half-byte */ + buffer[i++]=hexval(hb[j++]); + } + for (; hb[j] && ientries; i++) { + if (list->list[i].prefix == (address&list->list[i].mask)) 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); + } + snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, i); + return s; +} + +/* 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", + "PHASE_READCONFIG", + "PHASE_SETUP", + "PHASE_DROPPRIV", + "PHASE_RUN", + "PHASE_SHUTDOWN" +}; + +void enter_phase(uint32_t new_phase) +{ + struct phase_hook *i; + + Message(M_DEBUG_PHASE,"entering %s... ", phases[new_phase]); + current_phase=new_phase; + + for (i=hooks[new_phase]; i; i=i->next) + i->fn(i->state, new_phase); + Message(M_DEBUG_PHASE,"now in %s\n",phases[new_phase]); +} + +bool_t add_hook(uint32_t phase, hook_fn *fn, void *state) +{ + struct phase_hook *h; + + h=safe_malloc(sizeof(*h),"add_hook"); + h->fn=fn; + h->state=state; + h->next=hooks[phase]; + hooks[phase]=h; + return True; +} + +bool_t remove_hook(uint32_t phase, hook_fn *fn, void *state) +{ + fatal("remove_hook: not implemented\n"); + + return False; +} + +void log(struct log_if *lf, int priority, char *message, ...) +{ + va_list ap; + + va_start(ap,message); + lf->vlog(lf->st,priority,message,ap); + va_end(ap); +} + +struct buffer { + closure_t cl; + struct buffer_if ops; +}; + +void buffer_assert_free(struct buffer_if *buffer, string_t file, uint32_t line) +{ + if (!buffer->free) { + fatal("BUF_ASSERT_FREE, %s line %d, owned by %s\n", + file,line,buffer->owner); + } +} + +void buffer_assert_used(struct buffer_if *buffer, string_t file, uint32_t line) +{ + if (buffer->free) { + fatal("BUF_ASSERT_USED, %s line %d, last owned by %s\n", + file,line,buffer->owner); + } +} + +void buffer_init(struct buffer_if *buffer, uint32_t max_start_pad) +{ + buffer->start=buffer->base+max_start_pad; + buffer->size=0; +} + +void *buf_append(struct buffer_if *buf, uint32_t amount) { + void *p; + p=buf->start + buf->size; + buf->size+=amount; + return p; +} + +void *buf_prepend(struct buffer_if *buf, uint32_t amount) { + buf->size+=amount; + return buf->start-=amount; +} + +void *buf_unappend(struct buffer_if *buf, uint32_t amount) { + if (buf->size < amount) return 0; + return buf->start+(buf->size-=amount); +} + +void *buf_unprepend(struct buffer_if *buf, uint32_t amount) { + void *p; + p=buf->start; + buf->start+=amount; + buf->size-=amount; + return p; +} + +/* Append a two-byte length and the string to the buffer. Length is in + network byte order. */ +void buf_append_string(struct buffer_if *buf, string_t s) +{ + uint16_t len; + + len=strlen(s); + *(uint16_t *)buf_append(buf,2)=htons(len); + memcpy(buf_append(buf,len),s,len); +} + +void buffer_new(struct buffer_if *buf, uint32_t len) +{ + buf->free=True; + buf->owner=NULL; + buf->flags=0; + buf->loc.file=NULL; + buf->loc.line=0; + buf->size=0; + buf->len=len; + buf->start=NULL; + buf->base=safe_malloc(len,"buffer_new"); +} + +static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct buffer *st; + item_t *item; + dict_t *dict; + bool_t lockdown=False; + + st=safe_malloc(sizeof(*st),"buffer_apply"); + st->cl.description="buffer"; + st->cl.type=CL_BUFFER; + st->cl.apply=NULL; + st->cl.interface=&st->ops; + st->ops.free=True; + st->ops.owner=NULL; + st->ops.flags=0; + st->ops.loc=loc; + st->ops.size=0; + st->ops.len=DEFAULT_BUFFER_SIZE; + st->ops.start=NULL; + + /* First argument, if present, is buffer length */ + item=list_elem(args,0); + if (item) { + if (item->type!=t_number) { + cfgfatal(st->ops.loc,"buffer","first parameter must be a " + "number (buffer size)\n"); + } + st->ops.len=item->data.number; + if (st->ops.lenops.loc,"buffer","ludicrously small buffer size\n"); + } + if (st->ops.len>MAX_BUFFER_SIZE) { + cfgfatal(st->ops.loc,"buffer","ludicrously large buffer size\n"); + } + } + /* Second argument, if present, is a dictionary */ + item=list_elem(args,1); + if (item) { + if (item->type!=t_dict) { + cfgfatal(st->ops.loc,"buffer","second parameter must be a " + "dictionary\n"); + } + dict=item->data.dict; + lockdown=dict_read_bool(dict,"lockdown",False,"buffer",st->ops.loc, + False); + } + + st->ops.base=safe_malloc(st->ops.len,"buffer"); + if (lockdown) { + Message(M_WARNING,"buffer: XXX lockdown\n"); + } + + return new_closure(&st->cl); +} + +init_module util_module; +void util_module(dict_t *dict) +{ + add_closure(dict,"logfile",logfile_apply); + add_closure(dict,"sysbuffer",buffer_apply); +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..a43cb08 --- /dev/null +++ b/util.h @@ -0,0 +1,53 @@ +/* $Log: util.h,v $ + * Revision 1.1 1996/03/14 17:05:12 sde1000 + * Initial revision + * + */ + +#ifndef util_h +#define util_h + +#include +#include +#include "secnet.h" +#include "config.h" +#include + +extern uint32_t message_level; +extern uint32_t syslog_level; + +#define BUF_ASSERT_FREE(buf) do { buffer_assert_free((buf), \ + __FILE__,__LINE__); } \ +while(0) +#define BUF_ASSERT_USED(buf) do { buffer_assert_used((buf), \ + __FILE__,__LINE__); } \ +while(0) +#define BUF_ALLOC(buf,own) do { buffer_assert_free((buf),__FILE__,__LINE__); \ + (buf)->free=False; (buf)->owner=(own); (buf)->start=(buf)->base; \ + (buf)->size=0; } while(0) +#define BUF_FREE(buf) do { (buf)->free=True; } while(0) + +extern void buffer_assert_free(struct buffer_if *buffer, string_t file, + uint32_t line); +extern void buffer_assert_used(struct buffer_if *buffer, string_t file, + uint32_t line); +extern void buffer_new(struct buffer_if *buffer, uint32_t len); +extern void buffer_init(struct buffer_if *buffer, uint32_t max_start_pad); +extern void *buf_append(struct buffer_if *buf, uint32_t amount); +extern void *buf_prepend(struct buffer_if *buf, uint32_t amount); +extern void *buf_unappend(struct buffer_if *buf, uint32_t amount); +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); + +extern uint32_t write_mpbin(MP_INT *a, uint8_t *buffer, uint32_t buflen); + +extern struct log_if *init_log(list_t *loglist); + +#endif /* util_h */ -- 2.30.2