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