--- /dev/null
+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");
+ };
--- /dev/null
+.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
--- /dev/null
+#* 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
--- /dev/null
+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
--- /dev/null
+/*
+ * $Log$
+ */
+
+/* conffile.c - process the configuration file */
+
+/* #define DUMP_PARSE_TREE */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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; i<amount; i++) printf(" . ");
+}
+
+static void ptree_dump(struct p_node *n, uint32_t d)
+{
+ if (!n) {
+ printf("NULL\n");
+ return;
+ }
+
+ if (n->type<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);
+}
--- /dev/null
+/* the "incl" state is used for picking up the name of an include file */
+%x incl
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+<incl>[ \t]* /* eat the whitespace */
+<incl>[^ \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);
+ }
+<<EOF>> {
+ 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;
+
--- /dev/null
+/*
+ * $Log$
+ */
+
+#ifndef conffile_h
+#define conffile_h
+
+#include "secnet.h"
+
+extern dict_t *read_conffile(char *conffile);
+
+#endif /* conffile_h */
--- /dev/null
+%token TOK_STRING
+%token TOK_NUMBER
+%token TOK_KEY
+
+%start input
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}
--- /dev/null
+/*
+ * $Log$
+ */
+
+#ifndef conffile_internal_h
+#define conffile_internal_h
+
+#include <stdio.h>
+#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 */
--- /dev/null
+/* -*- c -*- */
+
+/* These are from config.h.bot, pasted onto the end of config.h.in. */
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#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 */
--- /dev/null
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+/***************************************************************************
+ *
+ * Part II Project, "A secure, private IP network"
+ * Stephen Early <sde1000@cam.ac.uk>
+ *
+ *
+ * $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 <sys/types.h> doesn't define. */
+#undef pid_t
+
+/* Define to `unsigned' if <sys/types.h> 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 <sys/cdefs.h>
+#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 */
--- /dev/null
+/***************************************************************************
+ *
+ * Part II Project, "A secure, private IP network"
+ * Stephen Early <sde1000@cam.ac.uk>
+ *
+ *
+ * $RCSfile$
+ *
+ * Description:
+ *
+ * Copyright: (C) Stephen Early 1995
+ *
+ * $Revision$
+ *
+ * $Date$
+ *
+ * $State$
+ *
+ ***************************************************************************/
+
+/* $Log$
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
--- /dev/null
+#! /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 <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:736: \"$ac_try\") 1>&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 <<EOF
+#line 837 "configure"
+#include "confdefs.h"
+#include <assert.h>
+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 <<EOF
+#line 854 "configure"
+#include "confdefs.h"
+#include <assert.h>
+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 <<EOF
+#line 871 "configure"
+#include "confdefs.h"
+#include <assert.h>
+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 <<EOF
+#line 907 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+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
+#line 932 "configure"
+#include "confdefs.h"
+#include <string.h>
+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
+#line 950 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+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 <<EOF
+#line 971 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#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 <<EOF
+#line 1094 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#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 <<EOF
+#line 1127 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#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 <<EOF
+#line 1160 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vprintf(); below. */
+#include <assert.h>
+/* 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 <<EOF
+#line 1212 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt(); below. */
+#include <assert.h>
+/* 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 <<EOF
+#line 1265 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this. */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->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 <<EOF
+#line 1342 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+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 <<EOF
+#line 1357 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+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 <<EOF
+#line 1388 "configure"
+#include "confdefs.h"
+main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+if { (eval echo configure:1401: \"$ac_link\") 1>&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 <<EOF
+#line 1434 "configure"
+#include "confdefs.h"
+/* 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 mpz_init_set_str();
+
+int main() {
+mpz_init_set_str()
+; return 0; }
+EOF
+if { (eval echo configure:1445: \"$ac_link\") 1>&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 <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lgmp2 $LIBS"
+
+else
+ echo "$ac_t""no" 1>&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 <<EOF
+#line 1481 "configure"
+#include "confdefs.h"
+/* 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 yywrap();
+
+int main() {
+yywrap()
+; return 0; }
+EOF
+if { (eval echo configure:1492: \"$ac_link\") 1>&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 <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lfl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&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 <<EOF
+#line 1528 "configure"
+#include "confdefs.h"
+/* 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 adns_init();
+
+int main() {
+adns_init()
+; return 0; }
+EOF
+if { (eval echo configure:1539: \"$ac_link\") 1>&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 <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ladns $LIBS"
+
+else
+ echo "$ac_t""no" 1>&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 <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/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 <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > 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 <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $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 <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $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 <<CEOF' >> $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 <<EOF
+
+EOF
+cat >> $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
+
--- /dev/null
+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)
--- /dev/null
+/***************************************************************************
+ *
+ * Part II Project, "A secure, private IP network"
+ * Stephen Early <sde1000@cam.ac.uk>
+ *
+ *
+ * $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 <stdio.h>
+#include <gmp.h>
+
+#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);
+}
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * 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 <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#include <string.h> /* for memcpy() */
+#include <sys/types.h> /* for stupid systems */
+#include <netinet/in.h> /* 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<<s | 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");
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 <ijackson@nyx.cs.du.edu>.
+ * 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 */
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef modules_h
+#define modules_h
+
+extern void init_builtin_modules(dict_t *dict);
+
+#endif /* modules_h */
--- /dev/null
+1024 35 154107175724781677184264293617887954015562225725852111745852699493257053099810379926047345975839848434403852210573185384327420788855664167034282567346429150999373740871227795773749618022407366186555483566435251279808390618987056868368084933125373643004284007109877210578088697520329039753099981203724057693543 steve@myrddin
--- /dev/null
+/* 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#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; i<l; i++) {
+ if (st->pending_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; i<c->networks->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);
+}
--- /dev/null
+/* $Log$
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#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);
+}
--- /dev/null
+/* Name resolution using adns */
+
+#include <errno.h>
+#include "secnet.h"
+#include <adns.h>
+
+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);
+}
--- /dev/null
+/***************************************************************************
+ *
+ * Part II Project, "A secure, private IP network"
+ * Stephen Early <sde1000@cam.ac.uk>
+ *
+ *
+ * $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 <stdio.h>
+#include <gmp.h>
+#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<datalen; i++) {
+ buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
+ buff[5+i*2]=hexchars[data[i]&0xf];
+ }
+ buff[4+datalen*2]=0;
+
+ for (i=datalen*2+4; i<msize; i++)
+ buff[i]='f';
+
+ buff[msize]=0;
+
+ mpz_set_str(&a, buff, 16);
+
+ mpz_powm(&b, &a, &st->d, &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<datalen; i++) {
+ buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
+ buff[5+i*2]=hexchars[data[i]&0xf];
+ }
+ buff[4+datalen*2]=0;
+
+ for (i=datalen*2+4; i<msize; i++)
+ buff[i]='f';
+
+ buff[msize]=0;
+
+ mpz_set_str(&a, buff, 16);
+
+ mpz_set_str(&b, signature, 16);
+
+ mpz_powm(&c, &b, &st->e, &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);
+}
--- /dev/null
+/* $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 <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <adns.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#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;
+}
+
--- /dev/null
+/* Core interface of secnet, to be used by all modules */
+
+#ifndef secnet_h
+#define secnet_h
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#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 */
--- /dev/null
+/*
+ * 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; i<keyLen/32; i++)
+ w[i]=keyMaterial[i];
+ if(keyLen<256)
+ w[i]=(keyMaterial[i]&((1L<<((keyLen&31)))-1))|(1L<<((keyLen&31)));
+ for(i++; i<8; i++)
+ w[i]=0;
+ for(i=8; i<16; i++)
+ w[i]=ROL(w[i-8]^w[i-5]^w[i-3]^w[i-1]^PHI^(i-8),11);
+ for(i=0; i<8; i++)
+ w[i]=w[i+8];
+ for(i=8; i<132; i++)
+ w[i]=ROL(w[i-8]^w[i-5]^w[i-3]^w[i-1]^PHI^i,11);
+
+ RND03(w[ 0], w[ 1], w[ 2], w[ 3], k[ 0], k[ 1], k[ 2], k[ 3]);
+ RND02(w[ 4], w[ 5], w[ 6], w[ 7], k[ 4], k[ 5], k[ 6], k[ 7]);
+ RND01(w[ 8], w[ 9], w[ 10], w[ 11], k[ 8], k[ 9], k[ 10], k[ 11]);
+ RND00(w[ 12], w[ 13], w[ 14], w[ 15], k[ 12], k[ 13], k[ 14], k[ 15]);
+ RND31(w[ 16], w[ 17], w[ 18], w[ 19], k[ 16], k[ 17], k[ 18], k[ 19]);
+ RND30(w[ 20], w[ 21], w[ 22], w[ 23], k[ 20], k[ 21], k[ 22], k[ 23]);
+ RND29(w[ 24], w[ 25], w[ 26], w[ 27], k[ 24], k[ 25], k[ 26], k[ 27]);
+ RND28(w[ 28], w[ 29], w[ 30], w[ 31], k[ 28], k[ 29], k[ 30], k[ 31]);
+ RND27(w[ 32], w[ 33], w[ 34], w[ 35], k[ 32], k[ 33], k[ 34], k[ 35]);
+ RND26(w[ 36], w[ 37], w[ 38], w[ 39], k[ 36], k[ 37], k[ 38], k[ 39]);
+ RND25(w[ 40], w[ 41], w[ 42], w[ 43], k[ 40], k[ 41], k[ 42], k[ 43]);
+ RND24(w[ 44], w[ 45], w[ 46], w[ 47], k[ 44], k[ 45], k[ 46], k[ 47]);
+ RND23(w[ 48], w[ 49], w[ 50], w[ 51], k[ 48], k[ 49], k[ 50], k[ 51]);
+ RND22(w[ 52], w[ 53], w[ 54], w[ 55], k[ 52], k[ 53], k[ 54], k[ 55]);
+ RND21(w[ 56], w[ 57], w[ 58], w[ 59], k[ 56], k[ 57], k[ 58], k[ 59]);
+ RND20(w[ 60], w[ 61], w[ 62], w[ 63], k[ 60], k[ 61], k[ 62], k[ 63]);
+ RND19(w[ 64], w[ 65], w[ 66], w[ 67], k[ 64], k[ 65], k[ 66], k[ 67]);
+ RND18(w[ 68], w[ 69], w[ 70], w[ 71], k[ 68], k[ 69], k[ 70], k[ 71]);
+ RND17(w[ 72], w[ 73], w[ 74], w[ 75], k[ 72], k[ 73], k[ 74], k[ 75]);
+ RND16(w[ 76], w[ 77], w[ 78], w[ 79], k[ 76], k[ 77], k[ 78], k[ 79]);
+ RND15(w[ 80], w[ 81], w[ 82], w[ 83], k[ 80], k[ 81], k[ 82], k[ 83]);
+ RND14(w[ 84], w[ 85], w[ 86], w[ 87], k[ 84], k[ 85], k[ 86], k[ 87]);
+ RND13(w[ 88], w[ 89], w[ 90], w[ 91], k[ 88], k[ 89], k[ 90], k[ 91]);
+ RND12(w[ 92], w[ 93], w[ 94], w[ 95], k[ 92], k[ 93], k[ 94], k[ 95]);
+ RND11(w[ 96], w[ 97], w[ 98], w[ 99], k[ 96], k[ 97], k[ 98], k[ 99]);
+ RND10(w[100], w[101], w[102], w[103], k[100], k[101], k[102], k[103]);
+ RND09(w[104], w[105], w[106], w[107], k[104], k[105], k[106], k[107]);
+ RND08(w[108], w[109], w[110], w[111], k[108], k[109], k[110], k[111]);
+ RND07(w[112], w[113], w[114], w[115], k[112], k[113], k[114], k[115]);
+ RND06(w[116], w[117], w[118], w[119], k[116], k[117], k[118], k[119]);
+ RND05(w[120], w[121], w[122], w[123], k[120], k[121], k[122], k[123]);
+ RND04(w[124], w[125], w[126], w[127], k[124], k[125], k[126], k[127]);
+ RND03(w[128], w[129], w[130], w[131], k[128], k[129], k[130], k[131]);
+
+ for(i=0; i<=32; i++)
+ for(j=0; j<4; j++)
+ key->subkeys[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;
+}
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/* site.c - manage communication with a remote network site */
+
+#include <stdio.h>
+#include <sys/mman.h>
+
+#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);
+}
--- /dev/null
+# 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. <defaults,otherdefaults>{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);
--- /dev/null
+# 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. <defaults,otherdefaults>{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);
--- /dev/null
+# 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");
+ };
+
+};
--- /dev/null
+/* 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 <stdio.h>
+#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 (keylen<REQUIRED_KEYLEN) {
+ Message(M_ERROR,"transform_create: insufficient key material supplied "
+ "(need %d bytes, got %d)\n",REQUIRED_KEYLEN,keylen);
+ return False;
+ }
+
+#if 0
+ {
+ int i;
+ printf("Setting key to: ");
+ for (i=0; i<keylen; i++)
+ printf("%02x",key[i]);
+ printf("\n");
+ }
+#endif /* 0 */
+
+ serpent_makekey(&ti->cryptkey,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; i<padlen-1; i++) {
+ if (*++padp != padlen) {
+ *errmsg="pkcs5: corrupted padding";
+ return 1;
+ }
+ }
+
+ /* Sequence number must be within max_skew of lastrecvseq; lastrecvseq
+ is only allowed to increase. */
+ seqnum=ntohl(*(uint32_t *)buf_unprepend(buf,4));
+ skew=seqnum-ti->lastrecvseq;
+ 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);
+}
--- /dev/null
+/* 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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#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);
+}
--- /dev/null
+/* $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 <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <values.h>
+#include <assert.h>
+#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<binsize; i++) {
+ buff[i*2]=hexdigits[(bin[i] & 0xf0) >> 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] && i<buflen; i++) {
+ buffer[i]=(hexval(hb[j])<<4)|hexval(hb[j+1]);
+ j+=2;
+ }
+ free(hb);
+ return i;
+}
+
+bool_t subnet_match(struct subnet_list *list, uint32_t address)
+{
+ uint32_t i;
+ for (i=0; i<list->entries; 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.len<MIN_BUFFER_SIZE) {
+ cfgfatal(st->ops.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);
+}
--- /dev/null
+/* $Log: util.h,v $
+ * Revision 1.1 1996/03/14 17:05:12 sde1000
+ * Initial revision
+ *
+ */
+
+#ifndef util_h
+#define util_h
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "secnet.h"
+#include "config.h"
+#include <gmp.h>
+
+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 */