diff options
127 files changed, 22950 insertions, 0 deletions
diff --git a/sbin/isakmpd/BUGS b/sbin/isakmpd/BUGS new file mode 100644 index 00000000000..2eabfe3c778 --- /dev/null +++ b/sbin/isakmpd/BUGS @@ -0,0 +1,50 @@ +$Id: BUGS,v 1.1 1998/11/15 00:03:47 niklas Exp $ + +Until we have a bug-tracking system setup, we might just add bugs to this +file: +------------------------------------------------------------------------------ +* message_drop frees the message, this is sometimes wrong and can cause + duplicate frees, for example when a proposal does not get chosen. [fixed] + +* Notifications should be their own exchanges, otherwise the IV gets + disturbed. [fixed] + +* We need a death timeout on half-ready SAs just like exchanges. At the + moment we leak SAs. + +* When we establish a phase 2 exchange we seem to get the wrong IV set, + according to SSH's logs. [fixed] + +* If a phase 1 SA negotiation exists with a cause that is to be sent in + a NOTIFY to the peer, we get multiple free calls on the cleanup of the + informational exchange. + +* IKE mandates that a HASH should be added to informational exchanges in + phase 2. + +* Message_send requires an exchange to exist, and potentially it tries to + encrypt a message multiple times when retransmitting. [fixed] + +* Multiple protocol proposals seems to fail. [fixed] + +* The initiator fails to match the responders choice of protocol suite with + the correct one of its own when several are offered. [fixed] + +* Duplicate specified sections is not detected. [fixed] + +* Quick mode establishments via UI using -P bind-addr gets "Address already in + use". + +* Not chosen proposals should be deleted from the protos list in the sa + structure. [fixed] + +* Setting SPIs generates "Invalid argument" errors due to one tunnel endpoint + being INADDR_ANY. [fixed] + +* ipsec_proto structs are never allocated. [fixed] + +* Remove SPIs of unused proposals. [fixed] + +* If the first proposal is turned down, the initiator gets confused. + +* Renegotiation after a failed phase 1 fails. diff --git a/sbin/isakmpd/DESIGN-NOTES b/sbin/isakmpd/DESIGN-NOTES new file mode 100644 index 00000000000..da3abe9181e --- /dev/null +++ b/sbin/isakmpd/DESIGN-NOTES @@ -0,0 +1,345 @@ +$Id: DESIGN-NOTES,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +General coding conventions +-------------------------- +GNU indentation, Max 80 characters per line, KNF comments, mem* instead of b*, +BSD copyright (GPL alternative?), one header per module specifying the API. +Multiple inclusion protection like this: + +#ifndef _HEADERNAME_H_ +#define _HEADERNAME_H_ + +... Here comes the bulk of the header ... + +#endif /* _HEADERNAME_H_ */ + +Start all files with RCS ID tags, i.e. + +/* $Id: DESIGN-NOTES,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +GCC -Wall clean, ANSI prototypes. System dependent facilities should be +named sysdep_* and be placed in sysdep.c. Primary target systems are OpenBSD +and Linux, but porting to Microsoft Windows variants should not be made +overly difficult. + +Note places which needs reconsiderations with comments starting with the +string "XXX", e.g. + +/* XXX Not implemented yet. */ + +TOC +--- +app.c Application support. +asn.c ASN.1 utilities. +asn_useful.c ASN.1. useful structure defintions. +cert.c Dispatching certificate related functions to the according + module based on the encoding. +conf.c Interface to isakmpd configuration. +constants.c Value to name map of constants.. +cookie.c Cookie generation. +crypto.c Generic cryptography. +dh.c Diffie-Hellman exchange logic. +doi.c Generic handling of different DOIs. +exchange.c Exchange state machinery. +exchange_num.cst + Some constants used for exhange scripts. +field.c Generic handling of fields. +genconstants.sh + Generate constant files from .cst source. +genfields.sh Generate field description files from .fld source. +gmp_util.c Utilities to ease interfaceing to GMP. +hash.c Generic hash handling. +if.c Network interface details. +ike_auth.c IKE authentication method abstraction. +ike_main_mode.c IKE's main mode exchange logic. +ike_quick_mode.c + IKE's quick mode logic. +init.c Initialization of all modules (might be autogenned in the + future). +ipsec.c The IPSec DOI. +ipsec_fld.fld Description of IPSec DOI-specific packet layouts. +ipsec_num.cst Constants defined by the IPSec DOI. +isakmp_doi.c The ISAKMP pseudo-DOI. +isakmp_fld.fld Generic packet layout. +isakmp_num.cst ISAKMP constants. +isakmpd.c Main loop. +log.c Logging of exceptional or informational messages. +math_2n.c Polynomial math. +math_ec2n.c Elliptic curve math. +math_group.c Group math. +message.c Generic message handling. +pf_encap.c Interface with PF_ENCAP sockets (for use with IPSEC). +pkcs.c PKCS#1: RSA Encryption Standard. +prf.c Pseudo random functions. +sa.c Handling of Security Associations (SAs). +sysdep-*.c System dependent definitions. +timer.c Timed events. +transport.c Generic transport handling. +udp.c The UDP transport. +ui.c The "User Interface", i.e. the FIFO command handler. +util.c Miscellaneous utility functions. +x509.c Encoding/Decoding X509 Certificates and related structures. + +Central datatypes +----------------- + +struct constant_map A map from constants to their ASCII names. +struct crypto_xf A crypto class +struct doi The DOI function switch +struct event An event that is to happen at some point in time. +struct exchange A description of an exchange while it is performed. +struct field A description of an ISAKMP field. +struct group A class abstracting out Oakley group operations +struct hash A hashing class +struct ipsec_exch IPSec-specific exchange fields. +struct ipsec_proto IPSec-specific protocol attributes. +struct ipsec_sa IPSec-specific SA stuff. +struct message A generic ISAKMP message. +struct payload A "fat" payload reference pointing into message buffers +struct prf A pseudo random function class +struct proto Per-protocol attributes. +struct post_send Post-send function chain node. +struct sa A security association. +struct transport An ISAKMP transport, i.e. a channel where ISAKMP + messages are passed (not necessarily connection- + oriented). This is an abstract class, serving as + a superclass to the different specific transports. + +SAs & exchanges +--------------- + +struct exchange Have all fields belonging to a simple exchange + + a list of all the SAs being negotiated. + Short-lived. +struct sa Only hold SA-specific stuff. Lives longer. + +In order to recognize exchanges and SAs it is good to know what constitutes +their identities: + +Phase 1 exchange Cookie pair (apart from the first message of course, + where the responder cookie is zero. + +ISAKMP SA Cookie pair. I.e. there exists a one-to-one + mapping to the negotiation in this case. + +Phase 2 exchange Cookie pair + message ID. + +Generic SA Cookie pair + message ID + SPI. + +However it would be really nice to have a name of any SA that is natural +to use for human beings, for things like deleteing SAs manually. The simplest +ID would be the struct sa address. Another idea would be some kind of sequence +number, either global or per-destination. + +The basic idea of control flow +------------------------------ + +The main loop just waits for events of any kind. Supposedly a message +comes in, then the daemon looks to see if the cookies describes an +existing ISAKMP SA, if they don't and the rcookie is zero, it triggers a +setup of a new ISAKMP SA. An exhaustive validation phase of the message +is gone through at this stage. If anything goes wrong, we drop the packet +and probably send some notification back. After the SA is found we try to +locate the exchange object and advance its state, else we try to create a +new exchange. + +Need exchanges be an abstraction visible in the code? If so an exchange is +roughly a very simple FSM (only timeouts and retransmissions are events that +does not just advance the state through a sequential single path). The +informational exchange is such a special case, I am not sure it's interesting +to treat as an exchange in the logic of the implementation. The only reason +to do so would be to keep the implementation tightly coupled to the +specification for ease of understanding. + +When the exchange has been found the exchange engine "runs" a script which +steps forward for each incoming message. + +Payload parsing details +----------------------- + +After the generic header has been validated, we do a generic payload +parsing pass over the message and sort out the payloads into buckets indexed +by the payload type. Note that proposals and transforms are part of the SA +payloads. We then pass over them once more validating each payload +in numeric payload type order. This makes SA payloads come naturally first. + +Messages +-------- + +I am not sure there is any use in sharing the message structure for both +incoming and outgoing messages but I do it anyhow. Specifically there are +certain fields which only makes sense in one direction. Incoming messages +only use one segment in the iovec vector, while outgoing has one segment per +payload as well as one for the ISAKMP header. The iovec vector is +reallocated for each payload added, maybe we should do it in chunks of a +number of payloads instead, like 10 or so. + +Design "errors" +--------------- + +Currently there are two "errors" in our design. The first one is that the +coupling between the IPSEC DOI and IKE is tight. It should be separated by +a clean interface letting other key exchange models fit in instead of IKE. +The second problem is that we need a protocol-specific opaque SA part +in the DOI specific one. Now both IPSEC ESP attributes takes place even +in ISAKMP SA structures. + +User control +------------ + +In order to control the daemon you send commands through a FIFO called +isakmpd.fifo. The commands are one-letter codes followed by arguments. +For now, only three commands are planned: + +c connect Establish an ISAKMP SA with a peer +d debug Toggle some debug flag +r report Report status information of the daemon + +For example you can do: + +c udp 127.0.0.1:555 2 1 + +to get an ISAKMP SA established with the ISAKMP implementation listening +on UDP port 555 at the local host. The SA will be established with an +identity protection (exchange type 2) exchange and use the IPSEC DOI (DOI 1). + +In order to delete an SA you use the 'd' command. However this is not yet +supported. + +To alter the level of debugging in the "LOG_MISC" logging class to 99 you do: + +D 0 99 + +The report command is just an "r", and results in a list of active exchanges +and security associations. + +The constant descriptions +------------------------- + +We have invented a simple constant description language, for the sake +of easily getting textual representations of manifest constants. +The syntax is best described by an example: + +GROUP + CONSTANT_A 1 + CONSTANT_B 2 +. + +This defines a constant map "group" with the following two defines: + +#define GROUP_CONSTANT_A 1 +#define GROUP_CONSTANT_B 2 + +We can now get the textual representation by: + + cp = constant_name (group, foo); + +Here foo is an integer with either of the two constants as a value. + +The field descriptions +---------------------- + +There is language for describing header and payload layouts too, +similar to the constant descriptions. Here too I just show an example: + +RECORD_A + FIELD_A raw 4 + FIELD_B num 2 + FIELD_C mask 1 group_c_cst + FIELD_D ign 1 + FIELD_E cst 2 group_e1_cst,group_e2_cst +. + +RECORD_B : RECORD_A + FIELD_F raw +. + +This creates some utility constants like RECORD_A_SZ, RECORD_A_FIELD_A_LEN, +RECORD_A_FIELD_A_OFF, RECORD_A_FIELD_B_LEN etc. The *_OFF contains the +octet offset into the record and the *_LEN constants are the lenghts. +The type fields can be: raw, num, mask, ign & cst. Raw are used for +octet buffers, num for (unsigned) numbers of 1, 2 or 4 octet's length +in network byteorder, mask is a bitmask where the bit values have symbols +coupled to them via the constant maps given after the length in octets +(also 1, 2 or 4). Ign is just a filler type, ot padding and lastly cst +denotes constants whose values can be found in the given constant map(s). +The last field in a record can be a raw, without a length, then just an +_OFF symbol will be generated. You can offset the first symbol to the +size of another record, like is done above for RECORD_B, i.e. in that +case RECORD_A_SZ == RECORD_B_FIELD_F_OFF. All this data are collected +in struct field arrays which makes it possible to symbolically print out +entire payloads in readable form via field_dump_payload. + +Identification +-------------- + +ISAKMP supports a lot of identity types, and we should too of course. + +* Main mode + +Today when we connect we do it based on the peer's IP address. That does not +automatically mean we should do policy decision based on IPs, rather we should +look at the ID the peer provide and get policy info keyed on that. + +Perhaps we get an ID saying the peer is FQDN niklas.hallqvist.se, then our +policy rules might look like: + +[IQ_FQDN] +# If commented, internal verification is used +#Verificator= verify_fqdn +Accept= no + +[ID_FQDN niklas.hallqvist.se] +Policy= MY_POLICY_001 + +[MY_POLICY_001] +# Whatever policy rules we might have. +Accept= yes + +Which means niklas.hallqvist.se is allowed to negotiate SAs with us, but +noone else. + +* Quick mode + +In quick mode the identities are implicitly the IP addresses of the peers, +which must mean the IP addresses actually used for the ISAKMP tunnel. + +License to use +-------------- +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +Maybe we should skip clause 3? Or redo it to mention the development was not +"by" but rather "funded by"? I think the comment about funding after the +license might also mention the actual author(s). Possibly the GPL should +be added as a 2ary distribution license to use if wanted? diff --git a/sbin/isakmpd/Makefile b/sbin/isakmpd/Makefile new file mode 100644 index 00000000000..9ab6b18e279 --- /dev/null +++ b/sbin/isakmpd/Makefile @@ -0,0 +1,74 @@ +# $Id: Makefile,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +PROG= isakmpd +SRCS= app.c asn.c asn_useful.c attribute.c cert.c constants.c \ + conf.c cookie.c crypto.c dh.c doi.c exchange.c exchange_num.c \ + field.c gmp_util.c hash.c if.c ike_auth.c ike_main_mode.c \ + ike_quick_mode.c init.c ipsec.c ipsec_fld.c ipsec_num.c \ + isakmpd.c isakmp_doi.c isakmp_fld.c isakmp_num.c log.c \ + message.c math_2n.c math_ec2n.c math_group.c pf_encap.c \ + pkcs.c prf.c sa.c sysdep.c timer.c transport.c udp.c ui.c \ + util.c x509.c +CLEANFILES= exchange_num.c exchange_num.h ipsec_num.c ipsec_num.h \ + isakmp_num.c isakmp_num.h ipsec_fld.c ipsec_fld.h \ + isakmp_fld.c isakmp_fld.h +MAN= isakmpd.8 isakmpd.conf.5 +LDADD= -lgmp -ldes +DPADD= ${LIBGMP} ${LIBDES} +CFLAGS+= -Wall -I. -I${.CURDIR} -DNEED_SYSDEP_APP +DEBUG= -g + +SUBDIR= regress + +exchange_num.c exchange_num.h: genconstants.sh exchange_num.cst + /bin/sh ${.CURDIR}/genconstants.sh ${.CURDIR}/exchange_num + +ipsec_fld.c ipsec_fld.h: genfields.sh ipsec_fld.fld + /bin/sh ${.CURDIR}/genfields.sh ${.CURDIR}/ipsec_fld + +ipsec_num.c ipsec_num.h: genconstants.sh ipsec_num.cst + /bin/sh ${.CURDIR}/genconstants.sh ${.CURDIR}/ipsec_num + +isakmp_fld.c isakmp_fld.h: genfields.sh isakmp_fld.fld + /bin/sh ${.CURDIR}/genfields.sh ${.CURDIR}/isakmp_fld + +isakmp_num.c isakmp_num.h: genconstants.sh isakmp_num.cst + /bin/sh ${.CURDIR}/genconstants.sh ${.CURDIR}/isakmp_num + +beforedepend: ipsec_fld.h ipsec_num.h isakmp_fld.h isakmp_num.h + +.include <bsd.prog.mk> +.include <bsd.subdir.mk> diff --git a/sbin/isakmpd/QUESTIONS b/sbin/isakmpd/QUESTIONS new file mode 100644 index 00000000000..2ac283565d2 --- /dev/null +++ b/sbin/isakmpd/QUESTIONS @@ -0,0 +1,33 @@ +$Id: QUESTIONS,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +Does the spec limit the count of SA payloads in a message? Where if so? +[ Only the specific IKE main mode does. In the IKE spec.] + +The message ID field of the header, can it be considered a SA identifier +if used together with the cookiepair? [Yes, it is meant to be that] + +DOI 0, what protocols are defined for it? Where? + +Isn't this a potential DOS attack: +Hostile user listens for ISAKMP traffic, and then extracts cookiepairs +and message IDs which he uses to flood any of the peers with spoofed +packets pretending to be the other one. Most probably these packets will +result in error notifications which potentially result in SA tear-down? +Maybe should notifications never be issued for erroneous packets which +cannot be authenticated? Or should we not tear down SAs as results of +notifications? + +Certicom claims to hold licenses for Elliptic Curve Cryptography? Does this +concern us? See: http://grouper.ieee.org/groups/p1363/patents.html + +Main mode when using public key encryption authentication does not look +like an identity protection exchange to me. Must I really get rid of +the generic ISAKMP payload presense tests? + +IV generation is not described precisely in Appendix B of -oakley-08.txt: +'Subsequent messages MUST use the last CBC encryption block from the previous +message as their IV'. This probably means that we take the new IV from the +last encrypted block of the last message we sent. The SSH testing site uses +the last block from the last message they received. This is probably not +what was meant and should be clarified on ipsec@tis.com. +[ From what we have gathered this is what is meant. ] diff --git a/sbin/isakmpd/README b/sbin/isakmpd/README new file mode 100644 index 00000000000..94ecc7602c9 --- /dev/null +++ b/sbin/isakmpd/README @@ -0,0 +1,59 @@ +$Id: README,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +This is isakmpd, a BSD-licensed ISAKMP/Oakley (a.k.a. IKE) +implementation. It's written by Niklas Hallqvist and Niels Provos, +funded by Ericsson Radio Systems AB. Currently it is just work in +progress, it cannot be used for anything real. For example it does not +renegotiate SAs when an application says they have expired. It is not +released, thus I won't bother calling it any version numbers. When you +got the source, hopefully the archive was named with a date which +reflects when it was created. These archives are also known as snapshots +and will be created at irregular intervals and put up on ftp.gsnig.net +and ftp.appli.se in /pub/isakmpd. + +Isakmpd is being developed under OpenBSD, with OpenBSD as its primary +target, soon enough however, a Linux effort will be started. Until +then the makefile support assumes a BSD environment. + +Assuming you have an OpenBSD /usr/share/mk and use the OpenBSD (or +similar) make(1), you build isakmpd this way: + +ln -s sysdep-openbsd.c sysdep.c +make obj && make depend && make + +Then obj/isakmpd will be the daemon. I suggest you try it by running +under gdb with args similar to: + -d -n -p5000 -D0=99 -D1=99 -D2=99 -D3=99 -D4=99 -D5=99 \ + -f/tmp/isakmpd.fifo -cisakmpd.conf.sample + +That will run isakmpd in the foreground, not connected to any application +(like an IPSEC implementation) logging to stderr with full debugging ouput, +listening on UDP port 5000, accepting control commands via the named pipe +called /tmp/isakmpd.fifo and reading its configuration from the +isakmpd.conf.sample file (found in the isakmpd directory). + +If you are root you can try to run without -n -p5000 thus getting it to +talk to your IPSec stack and use the standard port 500 instead. + +Read log.[ch] and ui.c to see how to alter the debugging levels. +Now you have setup your daemon and can watch incoming negotiations. +But how do you get such? Either use http://isakmp-test.ssh.fi/, +there's an excellent service, just waiting for you. Or you can try to +start another isakmpd on another port (say -p5001 or so, instead) +and another fifo (let's say /tmp/other.fifo), then issue this command: + +$ echo "c udp 127.0.0.1:5000 2 1" >/tmp/other.fifo + +and watch. You can turn on debugging on that isakmpd too of course, for +greater fun. When the ISAKMP SA is setup you can try quick mode too: + +$ echo "c udp 127.0.0.1:5000 32 1" >/tmp/other.fifo + +You will by now have noticed that this implementation is incomplete, but +who cares? You are here because you want to read code, start porting work +or help us out fixing what need's to be fixed. + +Happy IKEing! + +Niklas Hallqvist <niklas@openbsd.org> +Niels Provos <provos@openbsd.org> diff --git a/sbin/isakmpd/TO-DO b/sbin/isakmpd/TO-DO new file mode 100644 index 00000000000..35b9354072b --- /dev/null +++ b/sbin/isakmpd/TO-DO @@ -0,0 +1,78 @@ +$Id: TO-DO,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +This file is pretty lame as it should really contain a lot more given that +the program is far from ready in any area. + +* Add debugging messages, maybe possible to control asynchronously. [done] + +* Implement the local policy governing logging and notification of exceptional + conditions. + +* A field description mechanism used for things like making packet dumps + readable etc. Both Photurisd and Pluto does this. [done] + +* Fix the cookies. <Niels> [done] + +* Garbage collect transports (ref-counting?). + +* Retransmission/dup packet handling. [done] + +* Generic payload checks. [mostly done] + +* For math, speed up multiplication and division functions. + +* Cleanup of SAs when dropping messages. [done] + +* Look over message resource tracking. + +* Retransmission timing & count adaptivity and configurability. + [configurability done] + +* Quick mode exchanges [done] + +* Aggressive mode exchange. <Niels> + +* Finish main mode exchange [done] + +* Separation of key exchange from the IPSEC DOI, i.e. factor out IKE details. + +* Setup the IPSEC situation field in the main mode. [done] + +* Kernel interface for IPSEC parameter passing. + +* Notify of unsupported situations. + +* Set/get field macros generated from the field descriptions. [done] + +* SIGHUP handler with reparsing of config file. [done] + +* RSA signature authentication <Niels> [done] + +* DSS signature authentication + +* RSA encryption authentication + +* New group mode + +* DELETE payload handling, and generation from ui. + +* Deal well with incoming informational exchanges. + +* Generate all possible SA attributes in quick mode. [done] + +* Validate incoming attribute according to policy, main mode. + +* Validate incoming attribute according to policy, quick mode. + +* Cleanup reserved SPIs on cleanup of associated SAs. [done] + +* Validate attribute types (i.e. that what the specs tells should be + basic). + +* Cleanup reserved SPIs in proposals never chosen. [done] + +* Add time measuring and reporting to the exchange code for catching of + bottlenecks. + +* Rescan interfaces on SIGHUP and on reception of messages on the INADDR_ANY + listener socket. diff --git a/sbin/isakmpd/app.c b/sbin/isakmpd/app.c new file mode 100644 index 00000000000..c3eddb9b4eb --- /dev/null +++ b/sbin/isakmpd/app.c @@ -0,0 +1,65 @@ +/* $Id: app.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * XXX This is just a wrapper module for now. Later we might handle many + * applications simultaneously but right now, we assume one system-dependent + * one only. + */ + +#include "app.h" +#include "log.h" +#include "sysdep.h" + +int app_socket; + +/* Set this to not get any applications setup. */ +int app_none = 0; + +void +app_init () +{ + if (app_none) + return; + app_socket = sysdep_app_open (); + if (app_socket == -1) + log_fatal ("app_init: cannot open connection to application"); +} + +void +app_handler () +{ + sysdep_app_handler (app_socket); +} diff --git a/sbin/isakmpd/app.h b/sbin/isakmpd/app.h new file mode 100644 index 00000000000..c9d724c0366 --- /dev/null +++ b/sbin/isakmpd/app.h @@ -0,0 +1,45 @@ +/* $Id: app.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _APP_H_ +#define _APP_H_ + +extern int app_socket; +extern int app_none; + +extern void app_handler (void); +extern void app_init (void); + +#endif /* _APP_H_ */ diff --git a/sbin/isakmpd/asn.c b/sbin/isakmpd/asn.c new file mode 100644 index 00000000000..11e45882680 --- /dev/null +++ b/sbin/isakmpd/asn.c @@ -0,0 +1,1155 @@ +/* $Id: asn.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <gmp.h> +#include <unistd.h> + +#include "log.h" +#include "asn.h" +#include "gmp_util.h" + +struct asn_handler table[] = { + {TAG_INTEGER, asn_free_integer, + asn_get_encoded_len_integer, asn_decode_integer, asn_encode_integer}, + {TAG_OBJECTID, asn_free_objectid, + asn_get_encoded_len_objectid, asn_decode_objectid, asn_encode_objectid}, + {TAG_SEQUENCE, asn_free_sequence, + asn_get_encoded_len_sequence, asn_decode_sequence, asn_encode_sequence}, + {TAG_SET, asn_free_sequence, + asn_get_encoded_len_sequence, asn_decode_sequence, asn_encode_sequence}, + {TAG_UTCTIME, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_BITSTRING, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_OCTETSTRING, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_BOOL, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_PRINTSTRING, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_RAW, asn_free_raw, + asn_get_encoded_len_raw, asn_decode_raw, asn_encode_raw}, + {TAG_NULL, asn_free_null, + asn_get_encoded_len_null, asn_decode_null, asn_encode_null}, + {TAG_ANY, asn_free_null, + NULL, asn_decode_any, NULL}, + {TAG_STOP, NULL, NULL, NULL, NULL} +}; + +int +asn_get_from_file (char *name, u_int8_t **asn, u_int32_t *asnlen) +{ + int fd, res = 0; + struct stat st; + + if (stat (name, &st) == -1) + { + log_error ("asn_get_from_file: failed to state %s", name); + return 0; + } + + *asnlen = st.st_size; + + if ((fd = open (name, O_RDONLY)) == -1) + { + log_error ("asn_get_from_file: failed to open %s", name); + return 0; + } + + if ((*asn = malloc (st.st_size)) == NULL) + { + log_print ("asn_get_from_file: out of memory"); + res = 0; + goto done; + } + + if (read (fd, *asn, st.st_size) != st.st_size || + asn_get_len (*asn) != *asnlen) + { + log_print ("x509_asn_obtain: asn file ended early"); + free (*asn); + res = 0; + goto done; + } + + res = 1; + + done: + close (fd); + + return res; +} + +struct norm_type * +asn_template_clone (struct norm_type *obj, int constructed) +{ + struct norm_type *p; + u_int32_t i; + + if (!constructed) + { + p = malloc (sizeof (struct norm_type)); + if (p == NULL) + return NULL; + + memcpy (p, obj, sizeof (struct norm_type)); + + obj = p; + } + + if (obj->type != TAG_SEQUENCE && obj->type != TAG_SET) + { + obj->len = 0; + obj->data = NULL; + } + else if (obj->type == TAG_SEQUENCE || obj->type == TAG_SET) + { + p = obj; + obj = obj->data; + i = 0; + while (obj[i++].type != TAG_STOP); + + p->data = malloc (i * sizeof (struct norm_type)); + if (p->data == NULL) + return NULL; + + memcpy (p->data, obj, i * sizeof (struct norm_type)); + obj = p->data; + + i = 0; + while (obj[i].type != TAG_STOP) + { + obj[i].len = 0; + if (asn_template_clone (&obj[i], 1) == NULL) + return NULL; + + i++; + } + } + + return obj; +} + +/* Associates a human readable name to an OBJECT IDENTIFIER */ + +char * +asn_parse_objectid (struct asn_objectid *table, char *id) +{ + u_int32_t len = 0; + char *p = NULL; + static char buf[LINE_MAX]; + + if (id == NULL) + return NULL; + + while (table->name != NULL) + { + if (!strcmp (table->objectid, id)) + return table->name; + if (!strncmp (table->objectid, id, strlen (table->objectid)) && + strlen (table->objectid) > len) + { + len = strlen (table->objectid); + p = table->name; + } + + table++; + } + + if (len == 0) + return NULL; + + strncpy (buf, p, sizeof (buf) - 1); + buf[sizeof (buf) - 1] = 0; + strncat (buf + strlen (buf), id + len, sizeof (buf) -1 - strlen (buf)); + buf[sizeof (buf) - 1] = 0; + + return buf; +} + +/* Retrieves the pointer to a data type referenced by the path name */ + +struct norm_type * +asn_decompose (char *path, struct norm_type *obj) +{ + char *p, *p2, *tmp; + int counter; + + if (!strcasecmp (path, obj->name)) + return obj->data; + + p = path = strdup (path); + p2 = strsep (&p, "."); + + if (strcasecmp (p2, obj->name) || p == NULL) + goto fail; + + while (p != NULL) + { + obj = obj->data; + if (obj == NULL) + break; + + p2 = strsep (&p, "."); + + /* + * For SEQUENCE OF or SET OF, we want to be able to say + * AttributeValueAssertion[1] for the 2nd value. + */ + tmp = strchr (p2, '['); + if (tmp != NULL) + { + counter = atoi (tmp+1); + *tmp = 0; + } + else + counter = 0; + + /* Find the Tag */ + while (obj->type != TAG_STOP) + { + if (!strcasecmp (p2, obj->name) && counter-- == 0) + break; + obj++; + } + + if (obj->type == TAG_STOP) + goto fail; + + if (p == NULL) + goto done; + + if (obj->type != TAG_SEQUENCE && obj->type != TAG_SET) + goto fail; + } + + done: + free (path); + return obj; + + fail: + free (path); + return NULL; +} + +/* Gets an entry from the ASN.1 tag switch table */ + +struct asn_handler * +asn_get (enum asn_tags type) +{ + struct asn_handler *h = table; + + while (h->type != TAG_STOP) + if (h->type == type) + return h; + else + h++; + + return NULL; +} + +/* + * For the long form of BER encoding we need to know in how many + * octets the length can be encoded. + */ + +u_int32_t +asn_sizeinoctets (u_int32_t len) +{ + u_int32_t log = 0; + + while (len) + { + log++; + len >>= 8; + } + + return log; +} + +u_int8_t * +asn_format_header (struct norm_type *obj, u_int8_t *asn, u_int8_t **data) +{ + u_int8_t *buf = NULL, type; + u_int16_t len_off, len; + struct asn_handler *h; + + h = asn_get (obj->type); + if (h == NULL) + return NULL; + + if (asn != NULL) + buf = asn; + + /* We only do low tag at the moment */ + len_off = 1; + + len = h->get_encoded_len (obj, &type); + + if (buf == NULL && (buf = malloc (len)) == NULL) + return NULL; + + if (type != ASN_LONG_FORM) + { + len -= len_off + 1; + buf[len_off] = len; + + *data = buf + len_off + 1; + } + else + { + u_int16_t tmp; + int octets = asn_sizeinoctets (len); + + len -= len_off + 1 + octets; + *data = buf + len_off + 1 + octets; + + buf[len_off] = octets | ASN_LONG_FORM; + + tmp = len; + while (--octets >= 0) + { + buf[len_off + 1 + octets] = tmp; + tmp >>= 8; + } + } + + if (ISEXPLICIT(obj)) + { + u_int8_t *erg; + /* Explicit tagging add an outer layer */ + struct norm_type tmp = {obj->type, obj->class&0x3, NULL, 0, obj->data}; + + /* XXX - force the class to be CONTEXT */ + buf[0] = GET_EXP(obj) | (((enum asn_classes)CONTEXT & 0x3) << 6) | + ASN_CONSTRUCTED; + erg = asn_format_header (&tmp, *data, data); + + if (erg && (obj->type == TAG_SEQUENCE || obj->type == TAG_SET)) + erg[0] |= ASN_CONSTRUCTED; + } + else + /* XXX low tag only */ + buf[0] = obj->type | (obj->class << 6); + + return buf; +} + +u_int32_t +asn_get_encoded_len (struct norm_type *obj, u_int32_t len, u_int8_t *type) +{ + u_int32_t len_off = 1; + + if (len <= 127) + { + /* Short form */ + len = len + 1 + len_off; + if (type != NULL) + *type = 0; + } + else + { + /* Long Form */ + len = len + asn_sizeinoctets (len) + 1 + len_off; + if (type != NULL) + *type = ASN_LONG_FORM; + } + + if (obj != NULL && ISEXPLICIT(obj)) + len = asn_get_encoded_len (NULL, len, NULL); + + return len; +} + +/* Tries to decode an ANY tag, if we cant handle it we just raw encode it */ + +u_int8_t * +asn_decode_any (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + struct asn_handler *h; + enum asn_tags type; + + type = TAG_TYPE (asn); + if (type == TAG_SEQUENCE || type == TAG_SET) + type = TAG_RAW; + + h = asn_get (type); + if (h == NULL) + { + type = TAG_RAW; + h = asn_get (type); + } + + obj->type = type; + return h->decode (asn, asnlen, obj); +} + +u_int32_t +asn_get_encoded_len_integer (struct norm_type *obj, u_int8_t *type) +{ + u_int16_t len_off; + u_int32_t len = obj->len; + u_int32_t tmp; + mpz_t a; + + /* XXX - We only do low tag at the moment */ + len_off = 1; + + obj->len = len = mpz_sizeinoctets ((mpz_ptr) obj->data); + mpz_init_set (a, (mpz_ptr) obj->data); + + if (len > 1) + mpz_fdiv_q_2exp (a, a, (len - 1) << 3); + + tmp = mpz_fdiv_r_ui (a, a, 256); + mpz_clear (a); + + /* + * We only need to encode positive integers, ASN.1 defines + * negative integers to have the msb set, so if data[0] has + * msb set we need to introduce a zero octet. + */ + if (tmp & 0x80) + len++; + + return asn_get_encoded_len (obj, len, type); +} + +/* + * Encode an integer value. + * Input = obj, output = asn or return value. + */ + +u_int8_t * +asn_encode_integer (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf, *data; + u_int32_t len; + + buf = asn_format_header (obj, asn, &data); + + if (buf == NULL) + return NULL; + + len = mpz_sizeinoctets ((mpz_ptr) obj->data); + mpz_getraw (data, (mpz_ptr) obj->data, len); + + /* XXX - We only deal with unsigned integers at the moment */ + if (data[0] & 0x80) + { + memmove (data + 1, data, len); + data[0] = 0; + } + + return buf; +} + +u_int8_t * +asn_decode_integer (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *data; + u_int32_t len; + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_integer: ASN.1 content is bigger than buffer"); + return NULL; + } + + len = asn_get_data_len (obj, &asn, &data); + + if (TAG_TYPE(asn) != TAG_INTEGER) + { + log_print ("asn_decode_integer: expected tag type INTEGER, got %d", + TAG_TYPE(asn)); + return NULL; + } + + obj->data = malloc (sizeof (mpz_ptr)); + if (obj->data == NULL) + { + log_print ("asn_decode_integer: out of memory."); + return NULL; + } + + mpz_init ((mpz_ptr) obj->data); + mpz_setraw ((mpz_ptr) obj->data, data, len); + + obj->len = len; + + return data + len; +} + +void +asn_free_integer (struct norm_type *obj) +{ + if (obj->data != NULL) + { + mpz_clear ((mpz_ptr) obj->data); + free (obj->data); + } +} + + +u_int32_t +asn_get_encoded_len_string (struct norm_type *obj, u_int8_t *type) +{ + return asn_get_encoded_len (obj, obj->len, type); +} + +/* + * Encode a String + * Input = obj, output = asn or return value. + */ + +u_int8_t * +asn_encode_string (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf, *data; + + buf = asn_format_header (obj, asn, &data); + + if (buf == NULL) + return NULL; + + memcpy (data, obj->data, obj->len); + + return buf; +} + +u_int8_t * +asn_decode_string (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *data; + u_int32_t len; + + obj->len = len = asn_get_data_len (obj, &asn, &data); + + if (TAG_TYPE(asn) != obj->type) + { + log_print ("asn_decode_string: expected tag type STRING(%d), got %d", + obj->type, TAG_TYPE(asn)); + return NULL; + } + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_string: ASN.1 content is bigger than buffer"); + return NULL; + } + + obj->data = malloc (obj->len + 1); + if (obj->data == NULL) + return NULL; + memcpy ((char *)obj->data, data, obj->len); + /* + * Encode a terminating '0', this is irrelevant for OCTET strings + * but nice for printable strings which do not include the terminating + * zero. + */ + ((char *)obj->data)[obj->len] = 0; + + return data + len; +} + +void +asn_free_string (struct norm_type *obj) +{ + if (obj->data != NULL) + free (obj->data); +} + + +u_int32_t +asn_get_encoded_len_objectid (struct norm_type *obj, u_int8_t *type) +{ + u_int16_t len_off; + u_int32_t len; + u_int32_t tmp; + char *buf, *buf2; + + /* XXX - We only do low tag at the moment */ + len_off = 1; + + /* The first two numbers are encoded together */ + buf = obj->data; + tmp = strtol (buf, &buf2, 10); + buf = buf2; + tmp = strtol (buf, &buf2, 10); + buf = buf2; + + len = 1; + while (*buf) + { + tmp = strtol (buf, &buf2, 10); + if (buf == buf2) + break; + + buf = buf2; + do { + tmp >>= 7; + len++; + } while (tmp); + } + + /* The first two ids are encoded as one octet */ + obj->len = len - 1; + + return asn_get_encoded_len (obj, len, type); +} + +/* + * Encode an Object Identifier + * Input = obj, output = asn or return value. + */ + +u_int8_t * +asn_encode_objectid (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf, *data; + char *enc, *enc2; + u_int32_t tmp, tmp2; + int flag = 0; + + buf = asn_format_header (obj, asn, &data); + + if (buf == NULL) + return NULL; + + enc = obj->data; + while (*enc) + { + /* First two ids are encoded as one octet */ + if (flag == 0) + { + tmp = strtol (enc, &enc2, 10); + if (enc == enc2) + return NULL; + enc = enc2; + tmp2 = strtol (enc, &enc2, 10) + 40 * tmp; + flag = 1; + } + else + tmp2 = strtol (enc, &enc2, 10); + + if (enc == enc2) + break; + + /* Reverse the digits to base-128 */ + tmp = 0; + do { + tmp <<= 7; + tmp += tmp2 & 0x7f; + tmp2 >>= 7; + } while (tmp2); + + enc = enc2; + do { + /* If the next octet still belongs to the data set msb */ + *data++ = (tmp & 0x7f) | ( tmp > 127 ? 0x80 : 0); + tmp >>= 7; + } while (tmp); + } + + return buf; +} + +u_int8_t * +asn_decode_objectid (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *data; + u_int32_t len, c, tmp; + int flag = 0; + void *new_buf; + + len = asn_get_data_len (obj, &asn, &data); + + if (TAG_TYPE(asn) != TAG_OBJECTID) + { + log_print ("asn_decode_objectid: expected tag type OBJECTID, got %d", + TAG_TYPE(asn)); + return NULL; + } + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_objectid: ASN.1 content is bigger than buffer"); + return NULL; + } + + obj->data = NULL; + obj->len = 0; + while (len > 0) + { + tmp = 0; + do { + tmp <<= 7; + tmp += *data & 0x7f; + } while (len-- > 0 && (*data++ & 0x80)); + + if (flag == 0) + c = snprintf (NULL, 0, "%d %d ", tmp/40, tmp % 40) + 1; + else + c = snprintf (NULL, 0, "%d ", tmp) + 1; + + new_buf = realloc (obj->data, obj->len + c); + if (new_buf == NULL) + { + free (obj->data); + obj->data = NULL; + log_print ("asn_decode_objectid: out of memory."); + return NULL; + } + obj->data = new_buf; + + if (flag == 0) + { + sprintf (obj->data + obj->len, "%d %d ", tmp/40, tmp % 40); + flag = 1; + } + else + sprintf (obj->data + obj->len, "%d ", tmp); + + obj->len = strlen (obj->data); + } + + if (obj->data != NULL) + ((char *)obj->data)[obj->len - 1] = 0; + + return data; +} + +void +asn_free_objectid (struct norm_type *obj) +{ + if (obj->data != NULL) + free (obj->data); +} + + +u_int32_t +asn_get_encoded_len_raw (struct norm_type *obj, u_int8_t *type) +{ + if (type != NULL) + { + if (obj->len > 127) + *type = ASN_LONG_FORM; + else + *type = 0; + } + + return obj->len; +} + +u_int8_t * +asn_encode_raw (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf = NULL; + + if (obj->len == 0) + return asn; + + if (asn != NULL) + buf = asn; + + if (buf == NULL && (buf = malloc (obj->len)) == NULL) + return NULL; + + memcpy (buf, obj->data, obj->len); + + return buf; +} + +u_int8_t * +asn_decode_raw (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + obj->len = asn_get_len (asn); + if (asnlen < obj->len) + { + log_print ("asn_decode_raw: ASN.1 content is bigger than buffer"); + return NULL; + } + + obj->data = malloc (obj->len); + if (obj->data == NULL) + { + log_print ("asn_decode_raw: out of memory"); + return NULL; + } + + memcpy (obj->data, asn, obj->len); + + return asn + obj->len; +} + +void +asn_free_raw (struct norm_type *obj) +{ + if (obj->data != NULL) + free (obj->data); +} + +u_int32_t +asn_get_encoded_len_null (struct norm_type *obj, u_int8_t *type) +{ + return asn_get_encoded_len (obj, 0, type); +} + +u_int8_t * +asn_encode_null (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf = NULL; + + if (asn != NULL) + buf = asn; + + if (buf == NULL && (buf = malloc (2)) == NULL) + return NULL; + + buf[0] = obj->type; + buf[1] = 0; + + return buf; +} + +u_int8_t * +asn_decode_null (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + obj->data = NULL; + obj->len = 0; + + return asn + asn_get_len (asn); +} + +void +asn_free_null (struct norm_type *obj) +{ + obj->data = NULL; +} + +void +asn_free (struct norm_type *obj) +{ + struct asn_handler *h = asn_get (obj->type); + + if (h == NULL) + log_print ("asn_free: unkown ASN.1 type %d", obj->type); + else + h->free (obj); +} + +/* + * Returns the whole length of the BER encoded ASN.1 object. + */ + +u_int32_t +asn_get_len (u_int8_t *asn) +{ + u_int32_t len; + u_int8_t *data; + struct norm_type tmp = {TAG_RAW, UNIVERSAL, NULL, 0, NULL}; + + len = asn_get_data_len (&tmp, &asn, &data); + + if (asn == NULL) + return 0; + + return (data - asn) + len; +} + +/* + * Returns the length of the ASN content, and a pointer to the content + * data itself. + * For TAG_NULL the data length is zero, so we have to return an error + * in asn, asn will be NULL in case of error. + */ + +u_int32_t +asn_get_data_len (struct norm_type *obj, u_int8_t **asn, u_int8_t **data) +{ + u_int32_t len; + u_int16_t len_off = 1; + + if (obj != NULL && ISEXPLICIT(obj)) + { + struct norm_type tmp = {TAG_RAW, UNIVERSAL, NULL, 0, NULL}; + + if (TAG_TYPE(*asn) != GET_EXP(obj)) + { + log_print ("asn_get_data_len: explict tagging was needed"); + *asn = NULL; + return 0; + } + + asn_get_data_len (&tmp, asn, data); + *asn = *data; + } + + if ((*asn)[len_off] & ASN_LONG_FORM) + { + int i, octets = (*asn)[len_off] & 0x7f; + + /* XXX - we only decode really small length */ + if (octets > sizeof (len)) + { + log_print ("asn_get_data_len: long form length %d exceeds " + "allowed maximum", octets); + *asn = NULL; + return 0; + } + + for (len = 0, i = 0; i < octets; i++) + { + len = (len << 8) | (*asn)[len_off + 1 + i]; + } + + if (data != NULL) + *data = *asn + len_off + 1 + octets; + } + else + { + /* Short form */ + len = (*asn)[len_off]; + + if (data != NULL) + *data = *asn + len_off + 1; + } + + return len; +} + +void +asn_free_sequence (struct norm_type *obj) +{ + struct norm_type *in = obj->data; + struct asn_handler *h; + + if (in == NULL) + return; + + while (in->type != TAG_STOP) + { + h = asn_get (in->type); + if (h == NULL) + break; + + h->free (in++); + } + + free (obj->data); +} + +u_int32_t +asn_get_encoded_len_sequence (struct norm_type *seq, u_int8_t *type) +{ + u_int32_t len, i; + struct asn_handler *h; + struct norm_type *obj = (struct norm_type *) seq->data; + + /* Get whole data length */ + for (len = 0, i = 0; obj[i].type != TAG_STOP; i++) + { + h = asn_get (obj[i].type); + if (h == NULL) + { + log_print ("asn_encode_sequence: unkown type %d", obj[i].type); + break; + } + len += h->get_encoded_len (&obj[i], NULL); + } + + return asn_get_encoded_len (seq, len, type); +} + +u_int8_t * +asn_encode_sequence (struct norm_type *seq, u_int8_t *asn) +{ + u_int32_t len; + u_int8_t *erg, *data; + struct norm_type *obj; + struct asn_handler *h; + int i; + + if ((h = asn_get (seq->type)) == NULL) + return NULL; + + obj = (struct norm_type *) seq->data; + + erg = asn_format_header (seq, asn, &data); + if (erg == NULL) + return NULL; + + for (i = 0, len = 0; obj[i].type != TAG_STOP; i++) + { + h = asn_get (obj[i].type); + if (h == NULL) + { + log_print ("asn_encode_sequence: unknown ASN.1 tag %d", obj[i].type); + return NULL; + } + + /* A structure can be optional, indicated by data == NULL */ + if (h->encode (&obj[i], data + len) == NULL && obj->data != NULL) + { + log_print ("asn_encode_sequence: encoding of %s failed", + obj[i].name); + return NULL; + } + len += h->get_encoded_len (&obj[i], NULL); + } + + erg[0] |= ASN_CONSTRUCTED; + + return erg; +} + +u_int8_t * +asn_decode_sequence (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *p, *data; + u_int32_t len, flags, objects; + struct asn_handler *h; + void *new_buf; + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_sequence: ASN.1 content is bigger than buffer"); + return NULL; + } + + len = asn_get_data_len (obj, &asn, &data); + + /* XXX - an empty sequence is that okay */ + if (len == 0) + return data; + + if (TAG_TYPE(asn) != obj->type) + { + log_print ("asn_decode_sequence: expected tag type SEQUENCE/SET, got %d", + TAG_TYPE(asn)); + return NULL; + } + + /* Handle dynamic sized sets and sequences */ + flags = obj->flags; + + if (flags & ASN_FLAG_ZEROORMORE) + { + struct norm_type stop_tag = {TAG_STOP}; + struct norm_type *tmp; + + /* Zero occurences */ + if (len == 0) + { + asn_free (obj); + obj->data = NULL; + return data; + } + + /* Count number of objects */ + p = data; + objects = 0; + while (p < data + len) + { + objects++; + p += asn_get_len (p); + } + if (p != data + len) + { + log_print ("asn_decode_sequence: SEQ/SET OF too many elements"); + return NULL; + } + + /* + * Create new templates for dynamically added objects, + * the ASN.1 tags SEQUENCE OF and SET OF, specify an unknown + * number of elements. + */ + + new_buf = realloc (obj->data, + (objects+1) * sizeof (struct norm_type)); + if (new_buf == NULL) + { + asn_free (obj); + obj->data = NULL; + log_print ("asn_decode_sequence: out of memory"); + return NULL; + } + obj->data = new_buf; + + tmp = obj->data; + + /* Copy TAG_STOP */ + memcpy (tmp + objects, &stop_tag, sizeof (struct norm_type)); + while (objects-- > 1) + { + memcpy (tmp + objects, tmp, sizeof (struct norm_type)); + if (asn_template_clone (tmp + objects, 1) == NULL) + return NULL; + } + } + + obj = (struct norm_type *) obj->data; + + p = data; + while (p < data + len) + { + if (obj->type == TAG_STOP) + break; + h = asn_get (obj->type); + if (h == NULL) + { + log_print ("asn_decode_sequence: unknown ASN.1 tag %d", obj->type); + return NULL; + } + + if ((p = h->decode (p, (data - p) + len, obj++)) == NULL) + break; + } + + if (p < data + len) + log_print ("asn_decode_sequence: ASN tag was not decoded completely"); + + if (p == NULL) + return NULL; + + return data + len; +} diff --git a/sbin/isakmpd/asn.h b/sbin/isakmpd/asn.h new file mode 100644 index 00000000000..9a0e55f8936 --- /dev/null +++ b/sbin/isakmpd/asn.h @@ -0,0 +1,155 @@ +/* $Id: asn.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _ASN_H_ +#define _ASN_H_ + +/* Very very simple module for compiling ASN.1 BER encoding */ + +enum asn_classes { + UNIVERSAL = 0, + APPLICATION = 1, + CONTEXT = 2, + PRIVATE = 3 +}; + +#define TAG_EXPLICIT 4 +#define TAG_EXPSHIFTS 5 + +#define ISEXPLICIT(x) ((x)->class & TAG_EXPLICIT) +#define ADD_EXP(x,y) ((x << TAG_EXPSHIFTS) | TAG_EXPLICIT | (y)) +#define GET_EXP(x) ((x)->class >> TAG_EXPSHIFTS) + +enum asn_tags { + TAG_BOOL = 1, + TAG_INTEGER = 2, + TAG_BITSTRING = 3, + TAG_OCTETSTRING = 4, + TAG_NULL = 5, + TAG_OBJECTID = 6, /* Internal Representation as ASCII String */ + TAG_SEQUENCE = 16, + TAG_SET = 17, + TAG_PRINTSTRING = 19, + TAG_UTCTIME = 23, /* Represenation as ASCII String */ + TAG_STOP = -1, /* None official ASN tag, indicates end */ + TAG_RAW = -2, /* Placeholder for something we cant handle */ + TAG_ANY = -3, /* Either we can handle it or it is RAW */ +}; + +struct norm_type { + enum asn_tags type; + enum asn_classes class; + const char *name; + u_int32_t len; + void *data; + u_int32_t flags; +}; + +struct asn_objectid { + char *name; + char *objectid; +}; + +struct asn_handler { + enum asn_tags type; + void (*free) (struct norm_type *); + u_int32_t (*get_encoded_len) (struct norm_type *, u_int8_t *type); + u_int8_t *(*decode) (u_int8_t *, u_int32_t, struct norm_type *); + u_int8_t *(*encode) (struct norm_type *, u_int8_t *); +}; + +#define ASN_FLAG_ZEROORMORE 0x0001 + +/* Construct a Sequence */ +#define SEQ(x,y) {TAG_SEQUENCE, UNIVERSAL, x, 0, y} +#define SEQOF(x,y) {TAG_SEQUENCE, UNIVERSAL, x, 0, y, ASN_FLAG_ZEROORMORE} +#define SET(x,y) {TAG_SET, UNIVERSAL, x, 0, y} +#define SETOF(x,y) {TAG_SET, UNIVERSAL, x, 0, y, ASN_FLAG_ZEROORMORE} + +#define TAG_TYPE(x) ((enum asn_tags)((x)[0] & 0x1f)) + +/* Tag modifiers */ +#define ASN_CONSTRUCTED 0x20 /* Constructed object type */ + +/* Length modifiers */ +#define ASN_LONG_FORM 0x80 /* Number of length octets */ + +/* Function prototypes */ + +u_int8_t *asn_encode_integer (struct norm_type *, u_int8_t *); +u_int8_t *asn_decode_integer (u_int8_t *, u_int32_t, struct norm_type *); +u_int32_t asn_get_encoded_len_integer (struct norm_type *, u_int8_t *); +void asn_free_integer (struct norm_type *); + +u_int8_t *asn_encode_string (struct norm_type *, u_int8_t *); +u_int8_t *asn_decode_string (u_int8_t *, u_int32_t, struct norm_type *); +u_int32_t asn_get_encoded_len_string (struct norm_type *, u_int8_t *); +void asn_free_string (struct norm_type *); + +u_int8_t *asn_encode_objectid (struct norm_type *, u_int8_t *); +u_int8_t *asn_decode_objectid (u_int8_t *, u_int32_t, struct norm_type *); +u_int32_t asn_get_encoded_len_objectid (struct norm_type *, u_int8_t *); +void asn_free_objectid (struct norm_type *); + +u_int8_t *asn_encode_raw (struct norm_type *, u_int8_t *); +u_int8_t *asn_decode_raw (u_int8_t *, u_int32_t, struct norm_type *); +u_int32_t asn_get_encoded_len_raw (struct norm_type *, u_int8_t *); +void asn_free_raw (struct norm_type *); + +u_int8_t *asn_encode_null (struct norm_type *, u_int8_t *); +u_int8_t *asn_decode_null (u_int8_t *, u_int32_t, struct norm_type *); +u_int32_t asn_get_encoded_len_null (struct norm_type *, u_int8_t *); +void asn_free_null (struct norm_type *); + +u_int8_t *asn_encode_sequence (struct norm_type *, u_int8_t *); +u_int8_t *asn_decode_sequence (u_int8_t *, u_int32_t, struct norm_type *); +u_int32_t asn_get_encoded_len_sequence (struct norm_type *, u_int8_t *); +void asn_free_sequence (struct norm_type *); + +u_int8_t *asn_decode_any (u_int8_t *, u_int32_t, struct norm_type *); + +void asn_free (struct norm_type *); + +int asn_get_from_file (char *, u_int8_t **, u_int32_t *); +struct norm_type *asn_template_clone (struct norm_type *, int); + +u_int32_t asn_sizeinoctets (u_int32_t); +u_int32_t asn_get_len (u_int8_t *); +u_int32_t asn_get_data_len (struct norm_type *, u_int8_t **, u_int8_t **); +u_int32_t asn_get_encoded_len (struct norm_type *, u_int32_t, u_int8_t *); + +char *asn_parse_objectid (struct asn_objectid *, char *); +struct norm_type *asn_decompose (char *, struct norm_type *); +#endif /* _ASN_H_ */ diff --git a/sbin/isakmpd/asn_useful.c b/sbin/isakmpd/asn_useful.c new file mode 100644 index 00000000000..512009f81cb --- /dev/null +++ b/sbin/isakmpd/asn_useful.c @@ -0,0 +1,128 @@ +/* $Id: asn_useful.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> + +#include "asn.h" +#include "asn_useful.h" + +struct norm_type AlgorithmIdentifier[] = { + {TAG_OBJECTID, UNIVERSAL, "algorithm", 0, NULL}, + {TAG_ANY, UNIVERSAL, "parameters", 0, NULL}, + {TAG_STOP, UNIVERSAL, NULL, 0, NULL}}; + +struct norm_type Signed[] = { + {TAG_RAW, UNIVERSAL, "data", 0, NULL}, + SEQ("algorithm", AlgorithmIdentifier), + {TAG_BITSTRING, UNIVERSAL, "encrypted", 0, NULL}, + {TAG_STOP, UNIVERSAL, NULL, 0, NULL}}; + +struct norm_type Validity[] = { + {TAG_UTCTIME, UNIVERSAL, "notBefore", 0, NULL}, + {TAG_UTCTIME, UNIVERSAL, "notAfter", 0, NULL}, + {TAG_STOP, UNIVERSAL, NULL, 0, NULL}}; + +struct norm_type AttributeValueAssertion[] = { + {TAG_OBJECTID, UNIVERSAL, "AttributeType", 0, NULL}, + {TAG_ANY, UNIVERSAL, "AttributeValue", 0, NULL}, + {TAG_STOP, UNIVERSAL, NULL, 0, NULL}}; + +struct norm_type RelativeDistinguishedName[] = { + SEQ ("AttributeValueAssertion", AttributeValueAssertion), + {TAG_STOP}}; + +/* + * For decoding this structure is dynamically resized, we add two Names + * only for encoding purposes. + */ +struct norm_type RDNSequence[] = { + SETOF ("RelativeDistinguishedName", RelativeDistinguishedName), + SETOF ("RelativeDistinguishedName", RelativeDistinguishedName), + {TAG_STOP}}; + +struct norm_type SubjectPublicKeyInfo[] = { + SEQ ("algorithm", AlgorithmIdentifier), + {TAG_BITSTRING, UNIVERSAL, "subjectPublicKey", 0, NULL}, + {TAG_STOP}}; + +struct norm_type Extension[] = { + {TAG_OBJECTID, UNIVERSAL, "extnId", 0, NULL}, + {TAG_BOOL, UNIVERSAL, "critical", 0, NULL}, + {TAG_OCTETSTRING, UNIVERSAL, "extnValue", 0, NULL}, + {TAG_STOP}}; + +struct norm_type Extensions[] = { + SEQ ("extension", Extension), + {TAG_STOP}}; + +struct norm_type Certificate[] = { + /* We need to add an explicit tag, HACK XXX */ + {TAG_INTEGER, ADD_EXP(0, UNIVERSAL), "version", 0, NULL}, + {TAG_INTEGER, UNIVERSAL, "serialNumber", 0, NULL}, + SEQ ("signature", AlgorithmIdentifier), + SEQOF ("issuer", RDNSequence), + SEQ ("validity", Validity), + SEQOF ("subject", RDNSequence), + SEQ ("subjectPublicKeyInfo", SubjectPublicKeyInfo), + {TAG_RAW, UNIVERSAL, "extension", 0, NULL}, + {TAG_STOP}}; + +struct norm_type DigestInfo[] = { + SEQ ("digestAlgorithm", AlgorithmIdentifier), + {TAG_OCTETSTRING, UNIVERSAL, "digest", 0, NULL}, + {TAG_STOP}}; + +struct asn_objectid asn_ids[] = { + {"AttributeType", ASN_ID_ATTRIBUTE_TYPE}, + {"CountryName", ASN_ID_COUNTRY_NAME}, + {"LocalityName", ASN_ID_LOCALITY_NAME}, + {"StateOrProvinceName", ASN_ID_STATE_NAME}, + {"OrganizationName", ASN_ID_ORGANIZATION_NAME}, + {"OrganizationUnitName", ASN_ID_ORGUNIT_NAME}, + {"CommonUnitName", ASN_ID_COMMONUNIT_NAME}, + {"pkcs-1", ASN_ID_PKCS}, + {"rsaEncryption", ASN_ID_RSAENCRYPTION}, + {"md2WithRSAEncryption", ASN_ID_MD2WITHRSAENC}, + {"md4WithRSAEncryption", ASN_ID_MD4WITHRSAENC}, + {"md5WithRSAEncryption", ASN_ID_MD5WITHRSAENC}, + {"md2", ASN_ID_MD2}, + {"md4", ASN_ID_MD4}, + {"md5", ASN_ID_MD5}, + {"emailAddress", ASN_ID_EMAILADDRESS}, + {"id-ce", ASN_ID_CE}, + {"subjectAltName", ASN_ID_SUBJECT_ALT_NAME}, + {"issuerAltName", ASN_ID_ISSUER_ALT_NAME}, + {"basicConstraints", ASN_ID_BASIC_CONSTRAINTS}, + {NULL, NULL} }; diff --git a/sbin/isakmpd/asn_useful.h b/sbin/isakmpd/asn_useful.h new file mode 100644 index 00000000000..be32c825794 --- /dev/null +++ b/sbin/isakmpd/asn_useful.h @@ -0,0 +1,96 @@ +/* $Id: asn_useful.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _ASN_USEFUL_H_ +#define _ASN_USEFUL_H_ + +extern struct norm_type AlgorithmIdentifier[]; +extern struct norm_type Signed[]; +extern struct norm_type Validity[]; +extern struct norm_type RDNSequence[]; +extern struct norm_type Extensions[]; +extern struct norm_type Certificate[]; +extern struct norm_type DigestInfo[]; + +extern struct asn_objectid asn_ids[]; + +/* Accessing a SIGNED type */ +#define ASN_SIGNED(x) ((struct norm_type *)((x)->data)) +#define ASN_SIGNED_DATA(x) (ASN_SIGNED(x)->data) +#define ASN_SIGNED_ALGID(x) ((struct norm_type *)(ASN_SIGNED(x)+1)->data) +#define ASN_SIGNED_ENCRYPTED(x) (ASN_SIGNED(x)+2)->data + +#define ASN_SIGNED_ALGORITHM(x) (ASN_SIGNED_ALGID(x)->data) + +/* Accessing a Certificate */ +#define ASN_NT struct norm_type * +#define ASN_CERT(x) ((ASN_NT)((x)->data)) +#define ASN_CERT_VERSION(x) ASN_CERT(x)->data +#define ASN_CERT_SN(x) (ASN_CERT(x)+1)->data +#define ASN_CERT_ALGID(x) ((ASN_NT)((ASN_CERT(X)+2)->data)) +#define ASN_CERT_ALGORITHM(x) (ASN_CERT_ALGID(x)->data) +#define ASN_CERT_ISSUER(x) (ASN_CERT(x)+3) +#define ASN_CERT_VALIDITY(x) (ASN_CERT(x)+4) +#define ASN_CERT_SUBJECT(x) (ASN_CERT(x)+5) +#define ASN_CERT_PUBLICKEY(x) (ASN_CERT(x)+6) + +/* Accesing type Validity */ +#define ASN_VAL_BEGIN(x) (char *)(((ASN_NT)((x)->data))->data) +#define ASN_VAL_END(x) (char *)(((ASN_NT)((x)->data)+1)->data) + +#define ASN_ID_ATTRIBUTE_TYPE "2 5 4" +#define ASN_ID_COUNTRY_NAME ASN_ID_ATTRIBUTE_TYPE" 6" +#define ASN_ID_LOCALITY_NAME ASN_ID_ATTRIBUTE_TYPE" 7" +#define ASN_ID_STATE_NAME ASN_ID_ATTRIBUTE_TYPE" 8" +#define ASN_ID_ORGANIZATION_NAME ASN_ID_ATTRIBUTE_TYPE" 10" +#define ASN_ID_ORGUNIT_NAME ASN_ID_ATTRIBUTE_TYPE" 11" +#define ASN_ID_COMMONUNIT_NAME ASN_ID_ATTRIBUTE_TYPE" 3" + +#define ASN_ID_PKCS "1 2 840 113549 1 1" +#define ASN_ID_MD2 "1 2 840 113549 2 2" +#define ASN_ID_MD4 "1 2 840 113549 2 4" +#define ASN_ID_MD5 "1 2 840 113549 2 5" +#define ASN_ID_RSAENCRYPTION ASN_ID_PKCS" 1" +#define ASN_ID_MD2WITHRSAENC ASN_ID_PKCS" 2" +#define ASN_ID_MD4WITHRSAENC ASN_ID_PKCS" 3" +#define ASN_ID_MD5WITHRSAENC ASN_ID_PKCS" 4" + +#define ASN_ID_EMAILADDRESS "1 2 840 113549 1 9 1" + +#define ASN_ID_CE "2 5 29" +#define ASN_ID_SUBJECT_ALT_NAME ASN_ID_CE" 17" +#define ASN_ID_ISSUER_ALT_NAME ASN_ID_CE" 18" +#define ASN_ID_BASIC_CONSTRAINTS ASN_ID_CE" 19" +#endif /* _ASN_USEFUL_H_ */ diff --git a/sbin/isakmpd/attribute.c b/sbin/isakmpd/attribute.c new file mode 100644 index 00000000000..5acf8a37e32 --- /dev/null +++ b/sbin/isakmpd/attribute.c @@ -0,0 +1,112 @@ +/* $Id: attribute.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <string.h> + +#include "attribute.h" +#include "conf.h" +#include "log.h" +#include "isakmp.h" +#include "util.h" + +u_int8_t * +attribute_set_basic (u_int8_t *buf, u_int16_t type, u_int16_t value) +{ + SET_ISAKMP_ATTR_TYPE (buf, ISAKMP_ATTR_MAKE (1, type)); + SET_ISAKMP_ATTR_LENGTH_VALUE (buf, value); + return buf + ISAKMP_ATTR_VALUE_OFF; +} + +u_int8_t * +attribute_set_var (u_int8_t *buf, u_int16_t type, u_int8_t *value, + u_int16_t len) +{ + SET_ISAKMP_ATTR_TYPE (buf, ISAKMP_ATTR_MAKE (0, type)); + SET_ISAKMP_ATTR_LENGTH_VALUE (buf, len); + memcpy (buf + ISAKMP_ATTR_VALUE_OFF, value, len); + return buf + ISAKMP_ATTR_VALUE_OFF + len; +} + +/* Validate an area of ISAKMP attributes. */ +int +attribute_map (u_int8_t *buf, size_t sz, + int (*func) (u_int16_t, u_int8_t *, u_int16_t, void *), + void *arg) +{ + u_int8_t *attr; + int fmt; + u_int16_t type; + u_int8_t *value; + u_int16_t len; + + for (attr = buf; attr < buf + sz; attr = value + len) + { + if (attr + ISAKMP_ATTR_VALUE_OFF > buf + sz) + return -1; + type = GET_ISAKMP_ATTR_TYPE (attr); + fmt = ISAKMP_ATTR_FORMAT (type); + type = ISAKMP_ATTR_TYPE (type); + value + = attr + (fmt ? ISAKMP_ATTR_LENGTH_VALUE_OFF : ISAKMP_ATTR_VALUE_OFF); + len = (fmt ? ISAKMP_ATTR_LENGTH_VALUE_LEN + : GET_ISAKMP_ATTR_LENGTH_VALUE (attr)); + if (value + len > buf + sz) + return -1; + if (func (type, value, len, arg)) + return -1; + } + return 0; +} + +int +attribute_set_constant (char *section, char *tag, struct constant_map *map, + int attr_class, u_int8_t **attr) +{ + char *name; + int value; + + name = conf_get_str (section, tag); + if (!name) + { + /* XXX Should we really log hard like this? */ + log_print ("attribute_set_constant: no %s in the %s section", tag, + section); + return -1; + } + value = constant_value (map, name); + *attr = attribute_set_basic (*attr, attr_class, value); + return 0; +} diff --git a/sbin/isakmpd/attribute.h b/sbin/isakmpd/attribute.h new file mode 100644 index 00000000000..99b51b00029 --- /dev/null +++ b/sbin/isakmpd/attribute.h @@ -0,0 +1,52 @@ +/* $Id: attribute.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _ATTRIBUTE_H_ +#define _ATTRIBUTE_H_ + +#include <sys/types.h> + +struct constant_map; + +extern int attribute_map (u_int8_t *, size_t, + int (*) (u_int16_t, u_int8_t *, u_int16_t, void *), + void *); +extern u_int8_t *attribute_set_basic (u_int8_t *, u_int16_t, u_int16_t); +extern int attribute_set_constant (char *, char *, struct constant_map *, + int, u_int8_t **); +extern u_int8_t *attribute_set_var (u_int8_t *, u_int16_t, u_int8_t *, + u_int16_t); + +#endif /* _ATTRIBUTE_H_ */ diff --git a/sbin/isakmpd/cert.c b/sbin/isakmpd/cert.c new file mode 100644 index 00000000000..297b9c32681 --- /dev/null +++ b/sbin/isakmpd/cert.c @@ -0,0 +1,94 @@ +/* $Id: cert.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <stdlib.h> +#include <string.h> + +#include "cert.h" +#include "isakmp_num.h" +#include "x509.h" + +struct cert_handler cert_handler[] = { + {ISAKMP_CERTENC_X509_SIG, + x509_certreq_validate, x509_certreq_decode, x509_free_aca, + x509_cert_obtain, x509_cert_get_key, x509_cert_get_subject} +}; + +struct cert_handler * +cert_get (u_int16_t id) +{ + int i; + + for (i = 0; i < sizeof cert_handler / sizeof cert_handler[0]; i++) + if (id == cert_handler[i].id) + return &cert_handler[i]; + return NULL; +} + + +/* Decode a CERTREQ and return a parsed structure */ + +struct certreq_aca * +certreq_decode (u_int16_t type, u_int8_t *data, u_int32_t datalen) +{ + struct cert_handler *handler; + struct certreq_aca aca, *ret; + + if ((handler = cert_get (type)) == NULL) + return NULL; + + aca.id = type; + aca.handler = handler; + + if (datalen > 0) + { + aca.data = handler->certreq_decode (data, datalen); + if (aca.data == NULL) + return NULL; + } + else + aca.data = NULL; + + if ((ret = malloc (sizeof (aca))) == NULL) + { + handler->free_aca (aca.data); + return NULL; + } + + memcpy (ret, &aca, sizeof (aca)); + + return ret; +} diff --git a/sbin/isakmpd/cert.h b/sbin/isakmpd/cert.h new file mode 100644 index 00000000000..b4ff8701474 --- /dev/null +++ b/sbin/isakmpd/cert.h @@ -0,0 +1,68 @@ +/* $Id: cert.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _CERT_H_ +#define _CERT_H_ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/queue.h> + +struct exchange; + +struct cert_handler { + u_int16_t id; /* ISAKMP Cert Encoding ID */ + int (*certreq_validate) (u_int8_t *, u_int32_t); + void *(*certreq_decode) (u_int8_t *, u_int32_t); + void (*free_aca) (void *); + int (*cert_obtain) (struct exchange *, void *, u_int8_t **, u_int32_t *); + int (*cert_get_key) (u_int8_t *, u_int32_t, void *); + int (*cert_get_subject) (u_int8_t *, u_int32_t, u_int8_t **, u_int32_t *); +}; + +/* the acceptable authority of cert request */ + +struct certreq_aca { + TAILQ_ENTRY (certreq_aca) link; + + u_int16_t id; + struct cert_handler *handler; + void *data; /* if NULL everything is acceptable */ +}; + +struct cert_handler *cert_get (u_int16_t); +struct certreq_aca *certreq_decode (u_int16_t, u_int8_t *, u_int32_t); + +#endif /* _CERT_H_ */ diff --git a/sbin/isakmpd/conf.c b/sbin/isakmpd/conf.c new file mode 100644 index 00000000000..a05a9f9ec3b --- /dev/null +++ b/sbin/isakmpd/conf.c @@ -0,0 +1,426 @@ +/* $Id: conf.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "conf.h" +#include "log.h" + +/* + * Radix-64 Encoding. + */ + +const u_int8_t bin2asc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +const u_int8_t asc2bin[] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255 +}; + +struct conf_binding { + LIST_ENTRY (conf_binding) link; + char *section; + char *tag; + char *value; +}; + +char *conf_path = CONFIG_FILE; +LIST_HEAD (conf_bindings, conf_binding) conf_bindings; + +static off_t conf_sz; +static char *conf_addr; + +/* + * Insert a tag-value combination from LINE (the equal sign is at POS) + * into SECTION of our configuration database. + * XXX Should really be a hash table implementation. + */ +static void +conf_set (char *section, char *line, int pos) +{ + struct conf_binding *node; + int i; + + node = malloc (sizeof *node); + if (!node) + log_fatal ("conf_set: out of memory"); + node->section = section; + node->tag = line; + for (i = 0; line[i] && i < pos; i++) + ; + line[i] = '\0'; + if (conf_get_str (section, line)) + { + log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section, + line); + return; + } + node->value = line + pos + 1 + strspn (line + pos + 1, " \t"); + LIST_INSERT_HEAD (&conf_bindings, node, link); + log_debug (LOG_MISC, 70, "(%s,%s)->%s", node->section, node->tag, + node->value); +} + +/* + * Parse the line LINE of SZ bytes. Skip Comments, recognize section + * headers and feed tag-value pairs into our configuration database. + */ +static void +conf_parse_line (char *line, size_t sz) +{ + char *cp = line; + int i; + static char *section = 0; + static int ln = 0; + + ln++; + for (i = 0; line[i]; i++) + if (!isprint (*cp)) + { + log_print ("conf_parse_line: %d:" + "ignoring line %d with non-printable characters", ln); + return; + } + + /* Lines starting with '#' or ';' are comments. */ + if (*line == '#' || *line == ';') + return; + + /* '[section]' parsing... */ + if (*line == '[') + { + for (i = 1; i < sz; i++) + if (line[i] == ']') + break; + if (i == sz) + { + log_print ("conf_parse_line: %d:" + "non-matched ']', ignoring until next section", ln); + section = 0; + return; + } + section = malloc (i); + strncpy (section, line + 1, i - 1); + section[i - 1] = '\0'; + return; + } + + /* Deal with assignments. */ + for (i = 0; i < sz; i++) + if (cp[i] == '=') + { + /* If no section, we are ignoring the lines. */ + if (!section) + { + log_print ("conf_parse_line: %d: ignoring line due to no section", + ln); + return; + } + conf_set (section, line, i); + return; + } + + /* Other non-empty lines are wierd. */ + i = strspn (line, " \t"); + if (line[i]) + log_print ("conf_parse_line: %d: syntax error", ln); + + return; +} + +/* Parse the mapped configuration file. */ +static void +conf_parse (void) +{ + char *cp = conf_addr; + char *conf_end = conf_addr + conf_sz; + char *line; + + line = cp; + while (cp < conf_end) + { + if (*cp == '\n') + { + /* Check for escaped newlines. */ + if (cp > conf_addr && *(cp - 1) == '\\') + *(cp - 1) = *cp = ' '; + else + { + *cp = '\0'; + conf_parse_line (line, cp - line); + line = cp + 1; + } + } + cp++; + } + if (cp != line) + log_print ("conf_parse: last line non-terminated, ignored."); +} + +/* Open the config file and map it into our address space, then parse it. */ +void +conf_init (void) +{ + int fd; + struct stat st; + + /* + * Start by freeing potential existing configuration. + * + * XXX One could envision doing this late, surviving failures with just + * a warning log message that the new configuration did not get read + * and that the former one persists. + */ + if (conf_addr) + { + while (LIST_FIRST (&conf_bindings)) + LIST_REMOVE (LIST_FIRST (&conf_bindings), link); + free (conf_addr); + } + + fd = open (conf_path, O_RDONLY); + if (fd == -1) + log_fatal ("open (\"%s\", O_RDONLY)", conf_path); + if (fstat (fd, &st) == -1) + log_fatal ("fstat (%d, &st)", fd); + conf_sz = st.st_size; + conf_addr = malloc (conf_sz); + if (!conf_addr) + log_fatal ("malloc (%d)", conf_sz); + /* XXX I assume short reads won't happen here. */ + if (read (fd, conf_addr, conf_sz) != conf_sz) + log_fatal ("read (%d, %p, %d)", fd, conf_addr, conf_sz); + close (fd); + + LIST_INIT (&conf_bindings); + conf_parse (); +} + +/* Return the numeric value denoted by TAG in section SECTION. */ +int +conf_get_num (char *section, char *tag) +{ + char *value = conf_get_str (section, tag); + + if (value) + return atoi (value); + return 0; +} + +/* Return the string value denoted by TAG in section SECTION. */ +char * +conf_get_str (char *section, char *tag) +{ + struct conf_binding *cb; + + for (cb = LIST_FIRST (&conf_bindings); cb; cb = LIST_NEXT (cb, link)) + if (strcasecmp (section, cb->section) == 0 + && strcasecmp (tag, cb->tag) == 0) + { + log_debug (LOG_MISC, 60, "conf_get_str: (%s, %s) -> %s", section, + tag, cb->value); + return cb->value; + } + log_debug (LOG_MISC, 60, + "conf_get_str: configuration value not found (%s, %s)", section, + tag); + return 0; +} + +struct conf_list * +conf_get_list (char *section, char *tag) +{ + char *liststr = 0, *p, *field; + struct conf_list *list = 0; + struct conf_list_node *node; + + list = malloc (sizeof *list); + if (!list) + goto cleanup; + TAILQ_INIT (&list->fields); + list->cnt = 0; + liststr = conf_get_str (section, tag); + if (!liststr) + goto cleanup; + liststr = strdup (liststr); + if (!liststr) + goto cleanup; + p = liststr; + while ((field = strsep (&p, ", \t")) != NULL) + { + if (*field == '\0') + { + log_print ("conf_get_list: empty field, ignoring..."); + continue; + } + list->cnt++; + node = malloc (sizeof *node); + if (!node) + goto cleanup; + node->field = field; + TAILQ_INSERT_TAIL (&list->fields, node, link); + } + return list; + + cleanup: + if (list) + conf_free_list (list); + if (liststr) + free (liststr); + return 0; +} + +/* Decode a PEM encoded buffer. */ +int +conf_decode_base64(u_int8_t *out, u_int32_t *len, u_char *buf) +{ + u_int32_t c = 0; + u_int8_t c1, c2, c3, c4; + + while (*buf) + { + if (*buf > 127 || (c1 = asc2bin[*buf]) == 255) + return 0; + buf++; + + if (*buf > 127 || (c2 = asc2bin[*buf]) == 255) + return 0; + buf++; + + if (*buf == '=') + { + c3 = c4 = 0; + c++; + + /* Check last four bit */ + if (c2 & 0xF) + return 0; + + if (!strcmp (buf, "==")) + buf++; + else + return 0; + } + else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255) + return 0; + else + { + if (*++buf == '=') + { + c4 = 0; + c += 2; + + /* Check last two bit */ + if (c3 & 3) + return 0; + + if (strcmp(buf, "=")) + return 0; + + } + else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255) + return 0; + else + c += 3; + } + + buf++; + *out++ = (c1 << 2) | (c2 >> 4); + *out++ = (c2 << 4) | (c3 >> 2); + *out++ = (c3 << 6) | c4; + } + + *len = c; + return 1; + +} + +/* Read a line from a stream to the buffer. */ +int +conf_get_line (FILE *stream, char *buf, u_int32_t len) +{ + char c; + + while (len-- > 1) + { + c = fgetc (stream); + if (c == '\n') + { + *buf = 0; + return 1; + } + else if (c == EOF) + break; + + *buf++ = c; + } + + *buf = 0; + return 0; +} + +void +conf_free_list (struct conf_list *list) +{ + while (TAILQ_FIRST (&list->fields)) + TAILQ_REMOVE (&list->fields, TAILQ_FIRST (&list->fields), link); + free (list); +} diff --git a/sbin/isakmpd/conf.h b/sbin/isakmpd/conf.h new file mode 100644 index 00000000000..de8c2e821e3 --- /dev/null +++ b/sbin/isakmpd/conf.h @@ -0,0 +1,64 @@ +/* $Id: conf.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _CONF_H_ +#define _CONF_H_ + +#include <sys/queue.h> +#include <stdio.h> + +#define CONFIG_FILE "/etc/isakmpd.conf" + +struct conf_list_node { + TAILQ_ENTRY (conf_list_node) link; + char *field; +}; + +struct conf_list { + int cnt; + TAILQ_HEAD (conf_list_fields_head, conf_list_node) fields; +}; + +extern char *conf_path; + +extern void conf_init (void); +extern void conf_free_list (struct conf_list *); +extern struct conf_list *conf_get_list (char *, char *); +extern int conf_get_num (char *, char *); +extern char *conf_get_str (char *, char *); +extern int conf_get_line (FILE *, char *, u_int32_t); +extern int conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf); + +#endif /* _CONF_H_ */ diff --git a/sbin/isakmpd/constants.c b/sbin/isakmpd/constants.c new file mode 100644 index 00000000000..964cda17254 --- /dev/null +++ b/sbin/isakmpd/constants.c @@ -0,0 +1,92 @@ +/* $Id: constants.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <stdio.h> +#include <string.h> + +#include "constants.h" + +int +constant_value (struct constant_map *map, char *name) +{ + struct constant_map *entry = map; + + for (entry = map; entry->name; entry++) + if (strcasecmp (entry->name, name) == 0) + return entry->value; + return 0; +} + +char * +constant_lookup (struct constant_map *map, int value) +{ + struct constant_map *entry = map; + + for (entry = map; entry->name; entry++) + if (entry->value == value) + return entry->name; + return 0; +} + +char * +constant_name (struct constant_map *map, int value) +{ + static char tmp[32]; /* XXX Ugly, I know. */ + char *retval = constant_lookup (map, value); + + if (!retval) + { + snprintf (tmp, 32, "<Unknown %d>", value); + return tmp; + } + return retval; +} + +char * +constant_name_maps (struct constant_map **maps, int value) +{ + static char tmp[32]; /* XXX Ugly, I know. */ + char *retval; + struct constant_map **map; + + for (map = maps; *map; map++) + { + retval = constant_lookup (*map, value); + if (retval) + return retval; + } + snprintf (tmp, 32, "<Unknown %d>", value); + return tmp; +} diff --git a/sbin/isakmpd/constants.h b/sbin/isakmpd/constants.h new file mode 100644 index 00000000000..68efe7c41f3 --- /dev/null +++ b/sbin/isakmpd/constants.h @@ -0,0 +1,49 @@ +/* $Id: constants.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _CONSTANTS_H_ +#define _CONSTANTS_H_ + +struct constant_map { + int value; + char *name; +}; + +extern char *constant_lookup (struct constant_map *, int); +extern char *constant_name (struct constant_map *, int); +extern char *constant_name_maps (struct constant_map **, int); +extern int constant_value (struct constant_map *, char *); + +#endif /* _CONSTANTS_H_ */ diff --git a/sbin/isakmpd/cookie.c b/sbin/isakmpd/cookie.c new file mode 100644 index 00000000000..b31473957d6 --- /dev/null +++ b/sbin/isakmpd/cookie.c @@ -0,0 +1,127 @@ +/* $Id: cookie.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sha1.h> +#include <stdlib.h> +#include <string.h> + +#include "cookie.h" +#include "exchange.h" +#include "hash.h" +#include "log.h" +#include "timer.h" +#include "transport.h" +#include "util.h" + +#define COOKIE_EVENT_FREQ 360 +#define COOKIE_SECRET_SIZE 16 + +void cookie_secret_reset (void); + +u_int8_t cookie_secret[COOKIE_SECRET_SIZE]; + +/* + * Generate an anti-clogging token (a protection against an attacker forcing + * us to keep state for a flood of connection requests) a.k.a. a cookie + * at BUF, LEN bytes long. The cookie will be generated by hashing of + * information found, among otherplaces, in transport T and exchange + * EXCHANGE. + */ +void +cookie_gen (struct transport *t, struct exchange *exchange, u_int8_t *buf, + size_t len) +{ + struct hash* hash = hash_get (HASH_SHA1); + struct sockaddr *name; + int name_len; + + hash->Init (hash->ctx); + (*t->vtbl->get_dst) (t, &name, &name_len); + hash->Update (hash->ctx, (u_int8_t *)name, name_len); + (*t->vtbl->get_src) (t, &name, &name_len); + hash->Update (hash->ctx, (u_int8_t *)name, name_len); + if (exchange->initiator) + { + u_int8_t tmpsecret[COOKIE_SECRET_SIZE]; + + getrandom (tmpsecret, COOKIE_SECRET_SIZE); + hash->Update (hash->ctx, tmpsecret, COOKIE_SECRET_SIZE); + } + else + { + hash->Update (hash->ctx, exchange->cookies + ISAKMP_HDR_ICOOKIE_OFF, + ISAKMP_HDR_ICOOKIE_LEN); + hash->Update (hash->ctx, cookie_secret, COOKIE_SECRET_SIZE); + } + + hash->Final (hash->digest, hash->ctx); + memcpy (buf, hash->digest, len); +} + +/* + * Reset the secret which is used for the responder cookie. + * As responder we do not want to keep state in the cookie + * exchange, which means when the cookie secret is reset, + * our cookie response has timed out. + */ +void +cookie_secret_reset (void) +{ + getrandom (cookie_secret, COOKIE_SECRET_SIZE); +} + +/* + * Handle the cookie reset event, and reschedule with timer. + */ +void +cookie_reset_event (void *arg) +{ + struct timeval now; + + cookie_secret_reset (); + + gettimeofday (&now, 0); + now.tv_sec += COOKIE_EVENT_FREQ; + timer_add_event ("cookie_reset_event", cookie_reset_event, arg, &now); +} + +void +cookie_init (void) +{ + /* Start responder cookie resets. */ + cookie_reset_event (NULL); +} diff --git a/sbin/isakmpd/cookie.h b/sbin/isakmpd/cookie.h new file mode 100644 index 00000000000..b1ed008a5bf --- /dev/null +++ b/sbin/isakmpd/cookie.h @@ -0,0 +1,50 @@ +/* $Id: cookie.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _COOKIE_H_ +#define _COOKIE_H_ + +#include <sys/types.h> +#include <sys/socket.h> + +struct exchange; +struct transport; + +extern void cookie_gen (struct transport *, struct exchange *, u_int8_t *, + size_t); +extern void cookie_init (void); +extern void cookie_reset_event (void *); + +#endif /* _COOKIE_H_ */ diff --git a/sbin/isakmpd/crypto.c b/sbin/isakmpd/crypto.c new file mode 100644 index 00000000000..aba4002f06b --- /dev/null +++ b/sbin/isakmpd/crypto.c @@ -0,0 +1,355 @@ +/* $Id: crypto.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <stdlib.h> +#include <string.h> + +#include "crypto.h" +#include "log.h" + +enum cryptoerr des1_init (struct keystate *, u_int8_t *, u_int16_t); +enum cryptoerr des3_init (struct keystate *, u_int8_t *, u_int16_t); +enum cryptoerr blf_init (struct keystate *, u_int8_t *, u_int16_t); +enum cryptoerr cast_init (struct keystate *, u_int8_t *, u_int16_t); +void des1_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void des1_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void des3_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void des3_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void blf_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void blf_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void cast1_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void cast1_decrypt (struct keystate *, u_int8_t *, u_int16_t); + +struct crypto_xf transforms[] = { + { + DES_CBC, "Data Encryption Standard (CBC-Mode)", 8, 8, BLOCKSIZE, NULL, + des1_init, + des1_encrypt, des1_decrypt + }, + { + TRIPLEDES_CBC, "Triple-DES (CBC-Mode)", 24, 24, BLOCKSIZE, NULL, + des3_init, + des3_encrypt, des3_decrypt + }, + { + BLOWFISH_CBC, "Blowfish (CBC-Mode)", 12, 56, BLOCKSIZE, NULL, + blf_init, + blf_encrypt, blf_decrypt + }, + { + CAST_CBC, "CAST (CBC-Mode)", 12, 16, BLOCKSIZE, NULL, + cast_init, + cast1_encrypt, cast1_decrypt + }, +}; + +/* Hmm, the function prototypes for des are really dumb */ +#define DC (des_cblock *) + +enum cryptoerr +des1_init (struct keystate *ks, u_int8_t *key, u_int16_t len) +{ + /* des_set_key returns -1 for parity problems, and -2 for weak keys */ + des_set_odd_parity (DC key); + switch (des_set_key (DC key, ks->ks_des[0])) + { + case -2: + return EWEAKKEY; + default: + return EOKAY; + } +} + +void +des1_encrypt (struct keystate *ks, u_int8_t *d, u_int16_t len) +{ + des_cbc_encrypt (DC d, DC d, len, ks->ks_des[0], DC ks->riv, DES_ENCRYPT); +} + +void +des1_decrypt (struct keystate *ks, u_int8_t *d, u_int16_t len) +{ + des_cbc_encrypt (DC d, DC d, len, ks->ks_des[0], DC ks->riv, DES_DECRYPT); +} + +enum cryptoerr +des3_init (struct keystate *ks, u_int8_t *key, u_int16_t len) +{ + des_set_odd_parity (DC key); + des_set_odd_parity (DC key + 1); + des_set_odd_parity (DC key + 2); + + /* As of the draft Tripe-DES does not check for weak keys */ + des_set_key (DC key, ks->ks_des[0]); + des_set_key (DC key + 1, ks->ks_des[1]); + des_set_key (DC key + 2, ks->ks_des[2]); + + return EOKAY; +} + +void +des3_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int8_t iv[MAXBLK]; + + memcpy (iv, ks->riv, ks->xf->blocksize); + des_ede3_cbc_encrypt (DC data, DC data, len, ks->ks_des[0], ks->ks_des[1], + ks->ks_des[2], DC iv, DES_ENCRYPT); +} + +void +des3_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int8_t iv[MAXBLK]; + + memcpy (iv, ks->riv, ks->xf->blocksize); + des_ede3_cbc_encrypt (DC data, DC data, len, ks->ks_des[0], ks->ks_des[1], + ks->ks_des[2], DC iv, DES_DECRYPT); +} +#undef DC + +enum cryptoerr +blf_init (struct keystate *ks, u_int8_t *key, u_int16_t len) +{ + blf_key (&ks->ks_blf, key, len); + + return EOKAY; +} + +void +blf_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int16_t i, blocksize = ks->xf->blocksize; + u_int8_t *iv = ks->liv; + u_int32_t xl, xr; + + memcpy (iv, ks->riv, blocksize); + + for (i = 0; i < len; data += blocksize, i += blocksize) + { + XOR64 (data, iv); + xl = GET_32BIT_BIG (data); + xr = GET_32BIT_BIG (data + 4); + Blowfish_encipher (&ks->ks_blf, &xl, &xr); + SET_32BIT_BIG (data, xl); + SET_32BIT_BIG (data + 4, xr); + SET64 (iv, data); + } +} + +void +blf_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int16_t i, blocksize = ks->xf->blocksize; + u_int32_t xl, xr; + + data += len - blocksize; + for (i = len - blocksize; i >= blocksize; data -= blocksize, i -= blocksize) + { + xl = GET_32BIT_BIG (data); + xr = GET_32BIT_BIG (data + 4); + Blowfish_decipher (&ks->ks_blf, &xl, &xr); + SET_32BIT_BIG (data, xl); + SET_32BIT_BIG (data + 4, xr); + XOR64 (data, data - blocksize); + + } + xl = GET_32BIT_BIG (data); + xr = GET_32BIT_BIG (data + 4); + Blowfish_decipher (&ks->ks_blf, &xl, &xr); + SET_32BIT_BIG (data, xl); + SET_32BIT_BIG (data + 4, xr); + XOR64 (data, ks->riv); +} + +enum cryptoerr +cast_init (struct keystate *ks, u_int8_t *key, u_int16_t len) +{ + cast_setkey (&ks->ks_cast, key, len); + return EOKAY; +} + +void +cast1_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int16_t i, blocksize = ks->xf->blocksize; + u_int8_t *iv = ks->liv; + + memcpy (iv, ks->riv, blocksize); + + for (i = 0; i < len; data += blocksize, i += blocksize) + { + XOR64 (data, iv); + cast_encrypt (&ks->ks_cast, data, data); + SET64 (iv, data); + } +} + +void +cast1_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int16_t i, blocksize = ks->xf->blocksize; + + data += len - blocksize; + for (i = len - blocksize; i >= blocksize; data -= blocksize, i -= blocksize) + { + cast_decrypt (&ks->ks_cast, data, data); + XOR64 (data, data - blocksize); + } + cast_decrypt (&ks->ks_cast, data, data); + XOR64 (data, ks->riv); +} + +struct crypto_xf * +crypto_get (enum transform id) +{ + int i; + + for (i = 0; i < sizeof transforms / sizeof transforms[0]; i++) + if (id == transforms[i].id) + return &transforms[i]; + + return 0; +} + +struct keystate * +crypto_init (struct crypto_xf *xf, u_int8_t *key, u_int16_t len, + enum cryptoerr *err) +{ + struct keystate *ks = NULL; + + if (len < xf->keymin || len > xf->keymax) + { + log_debug (LOG_CRYPTO, 10, "crypto_init: invalid key length %d", len); + *err = EKEYLEN; + return NULL; + } + + if ((ks = calloc (1, sizeof (struct keystate))) == NULL) + { + *err = ENOCRYPTO; + return NULL; + } + + ks->xf = xf; + + /* Set up the iv */ + ks->riv = ks->iv; + ks->liv = ks->iv2; + + log_debug_buf (LOG_CRYPTO, 40, "crypto_init: key", key, len); + + *err = xf->init (ks, key, len); + if (*err != EOKAY) + { + log_debug (LOG_CRYPTO, 30, "crypto_init: weak key found for %s", + xf->name); + free (ks); + return NULL; + } + + return ks; +} + +void +crypto_update_iv (struct keystate *ks) +{ + u_int8_t *tmp; + + tmp = ks->riv; + ks->riv = ks->liv; + ks->liv = tmp; + + log_debug_buf (LOG_CRYPTO, 50, "crypto_update_iv: updated IV", ks->riv, + ks->xf->blocksize); +} + +void +crypto_init_iv (struct keystate *ks, u_int8_t *buf, size_t len) +{ + memcpy (ks->riv, buf, len); + + log_debug_buf (LOG_CRYPTO, 50, "crypto_update_iv: initialized IV", ks->riv, + len); +} + +void +crypto_encrypt (struct keystate *ks, u_int8_t *buf, u_int16_t len) +{ + log_debug_buf (LOG_CRYPTO, 10, "crypto_encrypt: before encryption", buf, + len); + ks->xf->encrypt (ks, buf, len); + memcpy (ks->liv, buf + len - ks->xf->blocksize, ks->xf->blocksize); + log_debug_buf (LOG_CRYPTO, 30, "crypto_encrypt: after encryption", buf, + len); +} + +void +crypto_decrypt (struct keystate *ks, u_int8_t *buf, u_int16_t len) +{ + log_debug_buf (LOG_CRYPTO, 10, "crypto_decrypt: before decryption", buf, + len); + /* + * XXX There is controversy about the correctness of updating the IV + * like this. + */ + memcpy (ks->liv, buf + len - ks->xf->blocksize, ks->xf->blocksize); + ks->xf->decrypt (ks, buf, len);; + log_debug_buf (LOG_CRYPTO, 30, "crypto_decrypt: after decryption", buf, + len); +} + +struct keystate * +crypto_clone_keystate (struct keystate *oks) +{ + struct keystate *ks; + + ks = malloc (sizeof *ks); + if (!ks) + return 0; + memcpy (ks, oks, sizeof *ks); + if (oks->riv == oks->iv) + { + ks->riv = ks->iv; + ks->liv = ks->iv2; + } + else + { + ks->riv = ks->iv2; + ks->liv = ks->iv; + } + return ks; +} diff --git a/sbin/isakmpd/crypto.h b/sbin/isakmpd/crypto.h new file mode 100644 index 00000000000..961ef232d14 --- /dev/null +++ b/sbin/isakmpd/crypto.h @@ -0,0 +1,145 @@ +/* $Id: crypto.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _CRYPTO_H_ +#define _CRYPTO_H_ + +#include <des.h> +#include <blf.h> +#include <cast.h> + +#define USE_32BIT +#if defined (USE_64BIT) + +#define XOR64(x,y) *(u_int64_t *)(x) ^= *(u_int64_t *)(y); +#define SET64(x,y) *(u_int64_t *)(x) = *(u_int64_t *)(y); + +#elif defined (USE_32BIT) + +#define XOR64(x,y) *(u_int32_t *)(x) ^= *(u_int32_t *)(y); \ + *(u_int32_t *)((u_int8_t *)(x) + 4) ^= *(u_int32_t *)((u_int8_t *)(y) + 4); +#define SET64(x,y) *(u_int32_t *)(x) = *(u_int32_t *)(y); \ + *(u_int32_t *)((u_int8_t *)(x) + 4) = *(u_int32_t *)((u_int8_t *)(y) + 4); + +#else + +#define XOR8(x,y,i) (x)[i] ^= (y)[i]; +#define XOR64(x,y) XOR8(x,y,0); XOR8(x,y,1); XOR8(x,y,2); XOR8(x,y,3); \ + XOR8(x,y,4); XOR8(x,y,5); XOR8(x,y,6); XOR8(x,y,7); +#define SET8(x,y,i) (x)[i] = (y)[i]; +#define SET64(x,y) SET8(x,y,0); SET8(x,y,1); SET8(x,y,2); SET8(x,y,3); \ + SET8(x,y,4); SET8(x,y,5); SET8(x,y,6); SET8(x,y,7); + +#endif /* USE_64BIT */ + +#define SET_32BIT_BIG(x,y) (x)[3]= (y); (x)[2]= (y) >> 8; \ + (x)[1] = (y) >> 16; (x)[0]= (y) >> 24; +#define GET_32BIT_BIG(x) (u_int32_t)(x)[3] | ((u_int32_t)(x)[2] << 8) | \ + ((u_int32_t)(x)[1] << 16)| ((u_int32_t)(x)[0] << 24); + +/* + * This is standard for all block ciphers we use at the moment. + * Theoretically this could increase in future, e.g. for TwoFish. + * Keep MAXBLK uptodate + */ +#define BLOCKSIZE 8 + +#define MAXBLK BLOCKSIZE + +struct keystate { + struct crypto_xf *xf; /* Back pointer */ + u_int16_t ebytes; /* Number of encrypted bytes */ + u_int16_t dbytes; /* Number of decrypted bytes */ + time_t life; /* Creation time */ + u_int8_t iv[MAXBLK]; /* Next IV to use */ + u_int8_t iv2[MAXBLK]; + u_int8_t *riv, *liv; + union { + des_key_schedule desks[3]; + blf_ctx blfks; + cast_key castks; + } keydata; +}; + +#define ks_des keydata.desks +#define ks_blf keydata.blfks +#define ks_cast keydata.castks + +/* + * Information about the cryptotransform. + * + * XXX - In regards to the IV (Initialization Vector) the drafts are + * completly fucked up and specify a MUST as how it is derived, so + * we also have to provide for that. I just don't know where. + * Furthermore is this enum needed at all? It seems to be Oakley IDs + * only anyhow, and we already have defines for that in ipsec_doi.h. + */ +enum transform { + DES_CBC=1, /* This is a MUST */ + IDEA_CBC=2, /* Licensed, DONT use */ + BLOWFISH_CBC=3, + RC5_R16_B64_CBC=4, /* Licensed, DONT use */ + TRIPLEDES_CBC=5, /* This is a SHOULD */ + CAST_CBC=6 +}; + +enum cryptoerr { + EOKAY, /* No error */ + ENOCRYPTO, /* A none crypto related error, see errno */ + EWEAKKEY, /* A weak key was found in key setup */ + EKEYLEN, /* The key length was invalid for the cipher */ +}; + +struct crypto_xf { + enum transform id; /* Oakley ID */ + char *name; /* Transform Name */ + u_int16_t keymin, keymax; /* Possible Keying Bytes */ + u_int16_t blocksize; /* Need to keep IV in the state */ + struct keystate *state; /* Key information, can also be passed sep. */ + enum cryptoerr (*init) (struct keystate *, u_int8_t *, u_int16_t); + void (*encrypt) (struct keystate *, u_int8_t *, u_int16_t); + void (*decrypt) (struct keystate *, u_int8_t *, u_int16_t); +}; + +extern struct keystate *crypto_clone_keystate (struct keystate *); +extern void crypto_decrypt (struct keystate *, u_int8_t *, u_int16_t); +extern void crypto_encrypt (struct keystate *, u_int8_t *, u_int16_t); +extern struct crypto_xf *crypto_get (enum transform); +extern struct keystate *crypto_init (struct crypto_xf *, u_int8_t *, + u_int16_t, enum cryptoerr *); +extern void crypto_init_iv (struct keystate *, u_int8_t *, size_t); +extern void crypto_update_iv (struct keystate *); + +#endif /* _CRYPTO_H_ */ diff --git a/sbin/isakmpd/dh.c b/sbin/isakmpd/dh.c new file mode 100644 index 00000000000..5062ad44b05 --- /dev/null +++ b/sbin/isakmpd/dh.c @@ -0,0 +1,79 @@ +/* $Id: dh.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> + +#include "math_group.h" +#include "dh.h" +#include "log.h" + +/* + * Returns the length of our exchange value. + */ + +int +dh_getlen (struct group *group) +{ + return group->getlen (group); +} + +/* + * Creates the exchange value we are offering to the other party. + * Each time this function is called a new value is created, that + * means the application has to save the exchange value itself, + * dh_create_exchange should only be called once. + */ + +void +dh_create_exchange (struct group *group, u_int8_t *buf) +{ + group->setrandom (group, group->c); + group->operation (group, group->a, group->gen, group->c); + group->getraw (group, group->a, buf); +} + +/* + * Creates the Diffie-Hellman shared secret in 'secret', where 'exchange' + * is the exchange value offered by the other party. No length verification + * is done for the value, the application has to do that. + */ + +void +dh_create_shared (struct group *group, u_int8_t *secret, u_int8_t *exchange) +{ + group->setraw (group, group->b, exchange, group->getlen(group)); + group->operation (group, group->a, group->b, group->c); + group->getraw (group, group->a, secret); +} diff --git a/sbin/isakmpd/dh.h b/sbin/isakmpd/dh.h new file mode 100644 index 00000000000..cf8c8a9ce69 --- /dev/null +++ b/sbin/isakmpd/dh.h @@ -0,0 +1,47 @@ +/* $Id: dh.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _DH_H_ +#define _DH_H_ + +#include <sys/types.h> + +struct group; + +int dh_getlen (struct group *); +void dh_create_exchange (struct group *, u_int8_t *); +void dh_create_shared (struct group *, u_int8_t *, u_int8_t *); + +#endif /* _DH_H_ */ diff --git a/sbin/isakmpd/doi.c b/sbin/isakmpd/doi.c new file mode 100644 index 00000000000..2724ddf5001 --- /dev/null +++ b/sbin/isakmpd/doi.c @@ -0,0 +1,64 @@ +/* $Id: doi.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> + +#include "doi.h" + +static LIST_HEAD (doi_list, doi) doi_tab; + +void +doi_init () +{ + LIST_INIT (&doi_tab); +} + +struct doi * +doi_lookup (u_int8_t doi_id) +{ + struct doi *doi; + + for (doi = LIST_FIRST (&doi_tab); doi && doi->id != doi_id; + doi = LIST_NEXT (doi, link)) + ; + return doi; +} + +void +doi_register (struct doi *doi) +{ + LIST_INSERT_HEAD (&doi_tab, doi, link); +} + diff --git a/sbin/isakmpd/doi.h b/sbin/isakmpd/doi.h new file mode 100644 index 00000000000..bc3a06f98f4 --- /dev/null +++ b/sbin/isakmpd/doi.h @@ -0,0 +1,92 @@ +/* $Id: doi.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _DOI_H_ +#define _DOI_H_ + +#include <sys/types.h> +#include <sys/queue.h> + +struct exchange; +struct keystate; +struct message; +struct proto; +struct sa; + +/* XXX This structure needs per-field commenting. */ +struct doi { + LIST_ENTRY (doi) link; + u_int8_t id; + + /* Size of DOI-specific exchange data. */ + size_t exchange_size; + + /* Size of DOI-specific security association data. */ + size_t sa_size; + + /* Size of DOI-specific protocol data. */ + size_t proto_size; + + int (*debug_attribute) (u_int16_t, u_int8_t *, u_int16_t, void *); + void (*delete_spi) (struct sa *, struct proto *, int); + u_int16_t *(*exchange_script) (u_int8_t); + void (*finalize_exchange) (struct message *); + void (*free_exchange_data) (void *); + void (*free_proto_data) (void *); + void (*free_sa_data) (void *); + struct keystate *(*get_keystate) (struct message *); + u_int8_t *(*get_spi) (size_t *, u_int8_t, struct message *); + int (*is_attribute_incompatible) (u_int16_t, u_int8_t *, u_int16_t, void *); + void (*setup_situation) (u_int8_t *); + size_t (*situation_size) (void); + u_int8_t (*spi_size) (u_int8_t); + int (*validate_attribute) (u_int16_t, u_int8_t *, u_int16_t, void *); + int (*validate_exchange) (u_int8_t); + int (*validate_id_information) (u_int8_t, u_int8_t *, u_int8_t *, size_t, + struct exchange *); + int (*validate_key_information) (u_int8_t *, size_t); + int (*validate_notification) (u_int16_t); + int (*validate_proto) (u_int8_t); + int (*validate_situation) (u_int8_t *, size_t *); + int (*validate_transform_id) (u_int8_t, u_int8_t); + int (*initiator) (struct message *msg); + int (*responder) (struct message *msg); +}; + +extern void doi_init (void); +extern struct doi *doi_lookup (u_int8_t); +extern void doi_register (struct doi *); + +#endif /* _DOI_H_ */ diff --git a/sbin/isakmpd/exchange.c b/sbin/isakmpd/exchange.c new file mode 100644 index 00000000000..b8d99386f71 --- /dev/null +++ b/sbin/isakmpd/exchange.c @@ -0,0 +1,936 @@ +/* $Id: exchange.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#include "cert.h" +#include "constants.h" +#include "cookie.h" +#include "crypto.h" +#include "doi.h" +#include "exchange.h" +#include "isakmp.h" +#include "log.h" +#include "message.h" +#include "timer.h" +#include "sa.h" +#include "util.h" + +/* Initial number of bits from the cookies used as hash. */ +#define INITIAL_BUCKET_BITS 6 + +/* + * Don't try to use more bits than this as a hash. + * We only XOR 16 bits so going above that means changing the code below + * too. + */ +#define MAX_BUCKET_BITS 16 + +static void exchange_dump (char *, struct exchange *); +static void exchange_free_aux (struct exchange *); + +static LIST_HEAD (exchange_list, exchange) *exchange_tab; + +/* Works both as a maximum index and a mask. */ +static int bucket_mask; + +/* + * Validation scripts used to test messages for correct content of + * payloads depending on the exchange type. + */ +int16_t script_base[] = { + ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ + ISAKMP_PAYLOAD_NONCE, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ + ISAKMP_PAYLOAD_NONCE, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_KEY_EXCH, /* Initiator -> responder. */ + ISAKMP_PAYLOAD_ID, + EXCHANGE_SCRIPT_AUTH, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_KEY_EXCH, /* Responder -> initiator. */ + ISAKMP_PAYLOAD_ID, + EXCHANGE_SCRIPT_AUTH, + EXCHANGE_SCRIPT_END +}; + +int16_t script_identity_protection[] = { + ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_KEY_EXCH, /* Initiator -> responder. */ + ISAKMP_PAYLOAD_NONCE, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_KEY_EXCH, /* Responder -> initiator. */ + ISAKMP_PAYLOAD_NONCE, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_ID, /* Initiator -> responder. */ + EXCHANGE_SCRIPT_AUTH, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_ID, /* Responder -> initiator. */ + EXCHANGE_SCRIPT_AUTH, + EXCHANGE_SCRIPT_END +}; + +int16_t script_authentication_only[] = { + ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ + ISAKMP_PAYLOAD_NONCE, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ + ISAKMP_PAYLOAD_NONCE, + ISAKMP_PAYLOAD_ID, + EXCHANGE_SCRIPT_AUTH, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_ID, /* Initiator -> responder. */ + EXCHANGE_SCRIPT_AUTH, + EXCHANGE_SCRIPT_END +}; + +int16_t script_aggressive[] = { + ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ + ISAKMP_PAYLOAD_KEY_EXCH, + ISAKMP_PAYLOAD_NONCE, + ISAKMP_PAYLOAD_ID, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ + ISAKMP_PAYLOAD_KEY_EXCH, + ISAKMP_PAYLOAD_NONCE, + ISAKMP_PAYLOAD_ID, + EXCHANGE_SCRIPT_AUTH, + EXCHANGE_SCRIPT_SWITCH, + EXCHANGE_SCRIPT_AUTH, /* Initiator -> responder. */ + EXCHANGE_SCRIPT_END +}; + +int16_t script_informational[] = { + EXCHANGE_SCRIPT_INFO, /* Initiator -> responder. */ + EXCHANGE_SCRIPT_END +}; + +/* + * Check what exchange SA is negotiated with and return a suitable validation + * script. + */ +u_int16_t * +exchange_script (struct exchange *exchange) +{ + switch (exchange->type) + { + case ISAKMP_EXCH_BASE: + return script_base; + case ISAKMP_EXCH_ID_PROT: + return script_identity_protection; + case ISAKMP_EXCH_AUTH_ONLY: + return script_authentication_only; + case ISAKMP_EXCH_AGGRESSIVE: + return script_aggressive; + case ISAKMP_EXCH_INFO: + return script_informational; + default: + if (exchange->type >= ISAKMP_EXCH_DOI_MIN + && exchange->type <= ISAKMP_EXCH_DOI_MAX) + return exchange->doi->exchange_script (exchange->type); + } + return 0; +} + +/* + * Validate the message MSG's contents wrt what payloads the exchange type + * requires at this point in the dialogoue. Return -1 if the validation fails, + * 0 if it succeeds and the script is not finished and 1 if it's ready. + */ +static int +exchange_validate (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + int16_t *pc = exchange->exch_pc; + + while (*pc != EXCHANGE_SCRIPT_END && *pc != EXCHANGE_SCRIPT_SWITCH) + { + log_debug (LOG_MISC, 90, "exchange_validate: checking for required %s", + *pc >= ISAKMP_PAYLOAD_NONE + ? constant_name (isakmp_payload_cst, *pc) + : constant_name (exchange_script_cst, *pc)); + + /* Check for existence of the required payloads. */ + if ((*pc > 0 && !TAILQ_FIRST (&msg->payload[*pc])) + || (*pc == EXCHANGE_SCRIPT_AUTH + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]) + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SIG])) + || (*pc == EXCHANGE_SCRIPT_INFO + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NOTIFY]) + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE]))) + { + /* Missing payload. */ + log_debug (LOG_MESSAGE, 70, + "exchange_validate: msg %p requires missing %s", msg, + *pc >= ISAKMP_PAYLOAD_NONE + ? constant_name (isakmp_payload_cst, *pc) + : constant_name (exchange_script_cst, *pc)); + return -1; + } + pc++; + } + if (*pc == EXCHANGE_SCRIPT_END) + /* Cleanup. */ + return 1; + + return 0; +} + +/* + * Run the exchange script from a point given by the "program counter" + * upto either the script's end or a transmittal of a message. If we are + * at the point of a reception of a message, that message should be handed + * in here in the MSG argument. Otherwise we are the initiator and should + * expect MSG to be a half-cooked message without payloads. + */ +void +exchange_run (struct message *msg) +{ + int i, done = 0; + struct exchange *exchange = msg->exchange; + int (*handler) (struct message *) = (exchange->initiator + ? exchange->doi->initiator + : exchange->doi->responder); + struct payload *payload; + + while (!done) + { + /* + * It's our turn if we're either the initiator on an even step, + * or the responder on an odd step of the dialogue. + */ + if (exchange->initiator ^ (exchange->step % 2)) + { + done = 1; + if (exchange->step) + msg = message_alloc_reply (msg); + message_setup_header (msg, exchange->type, 0, exchange->message_id); + if (handler (msg)) + { + /* + * This can happen when transient starvation of memory occurs. + * XXX The peer's retransmit ought to kick-start this exchange + * again. If he's stopped retransmitting he's likely dropped + * the SA at his side so we need to do that too, i.e. + * implement automatic SA teardown after a certain amount + * of inactivity. + */ + log_print ("exchange_run: exchange->doi->%s (%p) failed", + exchange->initiator ? "initiator" : "responder", msg); + message_free (msg); + return; + } + + switch (exchange_validate (msg)) + { + case 1: + /* + * The last message of an exchange should not be retransmitted. + * We should save this message in the ISAKMP SA if this is the + * final message of a phase 1 exchange. Then we can retransmit + * "on-demand" if we see retransmits of the last message of the + * peer later. + * XXX Think about this some more wrt the last message in + * phase 2 messages, does this not apply there too? + * MSG_NO_RETRANS and MSG_KEEP seems to go hand in hand btw.. + * Unify? + */ + msg->flags |= MSG_NO_RETRANS | MSG_KEEP; + if (msg->isakmp_sa) + { + if (msg->isakmp_sa->last_sent_in_setup) + message_free (msg->isakmp_sa->last_sent_in_setup); + msg->isakmp_sa->last_sent_in_setup = msg; + } + + /* + * After we physically have sent our last message we need to + * do SA-specific finalization, like telling our application + * the SA is ready to be used, or issuing a CONNECTED notify + * if we set the COMMIT bit. + */ + message_register_post_send (msg, exchange_finalize); + + /* Fallthrough. */ + + case 0: + /* XXX error handling. */ + message_send (msg); + break; + + default: + log_print ("exchange_run: exchange_validate failed, DOI error"); + exchange_free (exchange); + message_free (msg); + return; + } + } + else + { + done = exchange_validate (msg); + switch (done) + { + case 0: + case 1: + /* Feed the message to the DOI. */ + if (handler (msg)) + { + /* + * Trust the peer to retransmit. + * XXX We have to implement SA aging with automatic teardown. + */ + message_free (msg); + return; + } + + /* + * Go over the yet unhandled payloads and feed them to DOI + * for handling. + */ + for (i = ISAKMP_PAYLOAD_SA; i < ISAKMP_PAYLOAD_RESERVED_MIN; i++) + if (i != ISAKMP_PAYLOAD_PROPOSAL + && i != ISAKMP_PAYLOAD_TRANSFORM) + for (payload = TAILQ_FIRST (&msg->payload[i]); payload; + payload = TAILQ_NEXT (payload, link)) + if ((payload->flags & PL_MARK) == 0) + log_print ("exchange_run: unexpected payload %s", + constant_name (isakmp_payload_cst, i)); + + /* + * We have advanced the state. If we have been processing an + * incoming message, record that message as the one to do + * duplication tests against. + */ + if (exchange->last_received) + message_free (exchange->last_received); + exchange->last_received = msg; + if (exchange->flags & EXCHANGE_FLAG_ENCRYPT) + crypto_update_iv (exchange->keystate); + + if (done) + { + exchange_finalize (msg); + return; + } + break; + + case -1: + log_print ("exchange_run: exchange_validate failed"); + /* XXX Is this the best error notification type? */ + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 1); + return; + } + } + + log_debug (LOG_MISC, 40, "exchange_run: finished step %d, advancing...", + exchange->step); + exchange->step++; + while (*exchange->exch_pc != EXCHANGE_SCRIPT_SWITCH + && *exchange->exch_pc != EXCHANGE_SCRIPT_END) + exchange->exch_pc++; + exchange->exch_pc++; + } +} + +void +exchange_init () +{ + int i; + + bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1; + exchange_tab = malloc ((bucket_mask + 1) * sizeof (struct exchange_list)); + if (!exchange_tab) + log_fatal ("exchange_init: out of memory"); + for (i = 0; i <= bucket_mask; i++) + { + LIST_INIT (&exchange_tab[i]); + } + +} + +void +exchange_resize () +{ + int new_mask = (bucket_mask + 1) * 2 - 1; + int i; + struct exchange_list *new_tab; + + new_tab + = realloc (exchange_tab, (new_mask + 1) * sizeof (struct exchange_list)); + if (!new_tab) + return; + for (i = bucket_mask + 1; i <= new_mask; i++) + { + LIST_INIT (&new_tab[i]); + } + bucket_mask = new_mask; + /* XXX Rehash existing entries. */ +} + +/* Lookup a phase 1 exchange out of just the initiator cookie. */ +struct exchange * +exchange_lookup_from_icookie (u_int8_t *cookie) +{ + int i; + struct exchange *exchange; + + for (i = 0; i < bucket_mask; i++) + for (exchange = LIST_FIRST (&exchange_tab[i]); exchange; + exchange = LIST_NEXT (exchange, link)) + if (memcmp (exchange->cookies, cookie, ISAKMP_HDR_ICOOKIE_LEN) == 0 + && exchange->phase == 1) + return exchange; + return 0; +} + +int +exchange_enter (struct exchange *exchange) +{ + u_int16_t bucket = 0; + int i; + u_int8_t *cp; + + /* XXX We might resize if we are crossing a certain threshold */ + + for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) + { + cp = exchange->cookies + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) + { + cp = exchange->message_id + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + bucket &= bucket_mask; + LIST_INSERT_HEAD (&exchange_tab[bucket], exchange, link); + return 1; +} + +/* + * Lookup the exchange given by the header fields MSG. PHASE2 is false when + * looking for phase 1 exchanges and true otherwise. + */ +struct exchange * +exchange_lookup (u_int8_t *msg, int phase2) +{ + u_int16_t bucket = 0; + int i; + struct exchange *exchange; + u_int8_t *cp; + + /* + * We use the cookies to get bits to use as an index into exchange_tab, as at + * least one (our cookie) is a good hash, xoring all the bits, 16 at a + * time, and then masking, should do. Doing it this way means we can + * validate cookies very fast thus delimiting the effects of "Denial of + * service"-attacks using packet flooding. + */ + for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) + { + cp = msg + ISAKMP_HDR_COOKIES_OFF + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + if (phase2) + for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) + { + cp = msg + ISAKMP_HDR_MESSAGE_ID_OFF + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + bucket &= bucket_mask; + for (exchange = LIST_FIRST (&exchange_tab[bucket]); + exchange && (memcmp (msg + ISAKMP_HDR_COOKIES_OFF, exchange->cookies, + ISAKMP_HDR_COOKIES_LEN) != 0 + || (phase2 && memcmp (msg + ISAKMP_HDR_MESSAGE_ID_OFF, + exchange->message_id, + ISAKMP_HDR_MESSAGE_ID_LEN) != 0)); + exchange = LIST_NEXT (exchange, link)) + ; + + return exchange; +} + +/* + * Create a phase PHASE exchange where INITIATOR denotes our role. DOI + * is the domain of interpretation identifier and TYPE tells what exchange + * type to use per either the DOI document or the ISAKMP spec proper. + * NSA tells how many SAs we should pre-allocate, and should be zero + * when we have the responder role. + */ +static struct exchange * +exchange_create (int phase, int initiator, int doi, int type) +{ + struct exchange *exchange; + struct timeval expiration; + + /* + * We want the exchange zeroed for exchange_free to be able to find out + * what fields have been filled-in. + */ + exchange = calloc (1, sizeof *exchange); + if (!exchange) + return 0; + exchange->phase = phase; + exchange->step = 0; + exchange->initiator = initiator; + memset (exchange->cookies, 0, ISAKMP_HDR_COOKIES_LEN); + memset (exchange->message_id, 0, ISAKMP_HDR_MESSAGE_ID_LEN); + exchange->doi = doi_lookup (doi); + exchange->type = type; + exchange->exch_pc = exchange_script (exchange); + exchange->last_sent = exchange->last_received = 0; + TAILQ_INIT (&exchange->sa_list); + TAILQ_INIT (&exchange->aca_list); + + /* Allocate the DOI-specific structure and initialize it to zeroes. */ + exchange->data = calloc (1, exchange->doi->exchange_size); + if (!exchange->data) + { + exchange_free (exchange); + return 0; + } + + gettimeofday(&expiration, 0); + expiration.tv_sec += EXCHANGE_MAX_TIME; + exchange->death = timer_add_event ("exchange_free_aux", + (void (*) (void *))exchange_free_aux, + exchange, &expiration); + if (!exchange->death) + { + /* If we don't give up we might start leaking... */ + exchange_free (exchange); + return 0; + } + + return exchange; +} + +/* Establish a phase 1 exchange. */ +void +exchange_establish_p1 (struct transport *t, u_int8_t type, u_int32_t doi, + void *args) +{ + struct exchange *exchange; + struct message *msg; + + exchange = exchange_create (1, 1, doi, type); + if (!exchange) + { + /* XXX Do something here? */ + return; + } + cookie_gen (t, exchange, exchange->cookies, ISAKMP_HDR_ICOOKIE_LEN); + exchange_enter (exchange); + exchange_dump ("exchange_establish_p1", exchange); + + msg = message_alloc (t, 0, ISAKMP_HDR_SZ); + msg->exchange = exchange; + + /* + * Don't install a transport into this SA as it will be an INADDR_ANY + * address in the local end, which is not good at all. Let the reply + * packet install the transport instead. + */ + sa_create (exchange, 0); + msg->isakmp_sa = TAILQ_FIRST (&exchange->sa_list); + if (!msg->isakmp_sa) + { + /* XXX Do something more here? */ + exchange_free (exchange); + return; + } + + msg->extra = args; + + exchange_run (msg); +} + +/* Establish a phase 2 exchange. XXX With just one SA for now. */ +void +exchange_establish_p2 (struct sa *isakmp_sa, u_int8_t type, void *args) +{ + struct exchange *exchange; + struct message *msg; + int i; + + exchange = exchange_create (2, 1, isakmp_sa->doi->id, type); + if (!exchange) + { + /* XXX Do something here? */ + return; + } + memcpy (exchange->cookies, isakmp_sa->cookies, ISAKMP_HDR_COOKIES_LEN); + getrandom (exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + exchange->flags |= EXCHANGE_FLAG_ENCRYPT; + exchange_enter (exchange); + exchange_dump ("exchange_establish_p2", exchange); + + /* XXX Number of SAs should come from the args structure. */ + for (i = 0; i < 1; i++) + if (sa_create (exchange, isakmp_sa->transport)) + { + while (TAILQ_FIRST (&exchange->sa_list)) + TAILQ_REMOVE (&exchange->sa_list, TAILQ_FIRST (&exchange->sa_list), + next); + exchange_free (exchange); + return; + } + + msg = message_alloc (isakmp_sa->transport, 0, ISAKMP_HDR_SZ); + msg->isakmp_sa = isakmp_sa; + msg->extra = args; + + /* This needs to be done late or else get_keystate won't work right. */ + msg->exchange = exchange; + + exchange_run (msg); +} + +/* Out of an incoming phase 1 message, setup an exchange. */ +struct exchange * +exchange_setup_p1 (struct message *msg, u_int32_t doi) +{ + struct exchange *exchange; + + exchange = exchange_create (1, 0, doi, + GET_ISAKMP_HDR_EXCH_TYPE (msg->iov[0].iov_base)); + if (!exchange) + return 0; + cookie_gen (msg->transport, exchange, + exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN, + ISAKMP_HDR_RCOOKIE_LEN); + GET_ISAKMP_HDR_ICOOKIE (msg->iov[0].iov_base, exchange->cookies); + exchange_enter (exchange); + exchange_dump ("exchange_setup_p1", exchange); + return exchange; +} + +/* Out of an incoming phase 2 message, setup an exchange. */ +struct exchange * +exchange_setup_p2 (struct message *msg, u_int8_t doi) +{ + struct exchange *exchange; + u_int8_t *buf = msg->iov[0].iov_base; + + exchange = exchange_create (2, 0, doi, GET_ISAKMP_HDR_EXCH_TYPE (buf)); + if (!exchange) + return 0; + GET_ISAKMP_HDR_ICOOKIE (buf, exchange->cookies); + GET_ISAKMP_HDR_RCOOKIE (buf, exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN); + GET_ISAKMP_HDR_MESSAGE_ID (buf, exchange->message_id); + exchange_enter (exchange); + exchange_dump ("exchange_setup_p2", exchange); + return exchange; +} + +static void +exchange_dump (char *header, struct exchange *exchange) +{ + log_debug (LOG_MISC, 10, + "%s: %s phase %d doi %d exchange %d step %d msgid %08x", + header, exchange->initiator ? "initiator" : "responder", + exchange->phase, exchange->doi->id, exchange->type, + exchange->step, decode_32 (exchange->message_id)); + log_debug (LOG_MISC, 10, + "%s: icookie %08x%08x rcookie %08x%08x", header, + decode_32 (exchange->cookies), decode_32 (exchange->cookies + 4), + decode_32 (exchange->cookies + 8), + decode_32 (exchange->cookies + 12)); +} + +void +exchange_report (void) +{ + int i; + struct exchange *exchange; + + for (i = 0; i < bucket_mask; i++) + for (exchange = LIST_FIRST (&exchange_tab[i]); exchange; + exchange = LIST_NEXT (exchange, link)) + exchange_dump ("exchange_report", exchange); +} + +/* + * Release all resources this exchange is using *except* for the "death" + * event. When removing an exchange from the expiration handler that event + * will be dealt with therein instead. + */ +static void +exchange_free_aux (struct exchange *exchange) +{ + if (exchange->last_received) + message_free (exchange->last_received); + if (exchange->last_sent) + message_free (exchange->last_sent); + if (exchange->nonce_i) + free (exchange->nonce_i); + if (exchange->nonce_r) + free (exchange->nonce_r); + if (exchange->id_i) + free (exchange->id_i); + if (exchange->id_r) + free (exchange->id_r); + if (exchange->keystate) + free (exchange->keystate); + if (exchange->doi && exchange->doi->free_exchange_data) + exchange->doi->free_exchange_data (exchange->data); + if (exchange->data) + free (exchange->data); + exchange_free_aca_list (exchange); + LIST_REMOVE (exchange, link); + free (exchange); +} + +/* Release all resources this exchange is using. */ +void +exchange_free (struct exchange *exchange) +{ + if (exchange->death) + timer_remove_event (exchange->death); + exchange_free_aux (exchange); +} + +/* + * Upgrade the phase 1 exchange and its ISAKMP SA with the rcookie of our + * peer (found in his recently sent message MSG). + */ +void +exchange_upgrade_p1 (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + + LIST_REMOVE (exchange, link); + GET_ISAKMP_HDR_RCOOKIE (msg->iov[0].iov_base, + exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN); + exchange_enter (exchange); + sa_isakmp_upgrade (msg); +} + +void +exchange_finalize (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct sa *sa; + struct proto *proto; + int i; + + exchange_dump ("exchange_finalize", exchange); + + /* + * Walk over all the SAs and noting them as ready. If we set the COMMIT + * bit, tell the peer each SA is connected. + * XXX The decision should really be based on if a SA was installed + * successfully. + */ + for (sa = TAILQ_FIRST (&exchange->sa_list); sa; sa = TAILQ_NEXT (sa, next)) + { + if (exchange->flags & EXCHANGE_FLAG_I_COMMITTED) + { + for (proto = TAILQ_FIRST (&sa->protos); proto; + proto = TAILQ_NEXT (proto, link)) + for (i = 0; i < 2; i++) + message_send_notification (exchange->last_received, + msg->isakmp_sa, + ISAKMP_NOTIFY_STATUS_CONNECTED, proto, + i); + } + sa->flags |= SA_FLAG_READY; + sa->exch_type = exchange->type; + } + + /* + * If this was an phase 1 SA negotiation, save the keystate in the ISAKMP SA + * structure for future initialization of phase 2 exchanges' keystates. + */ + if (exchange->phase == 1 && msg->isakmp_sa) + { + msg->isakmp_sa->keystate = exchange->keystate; + exchange->keystate = 0; + } + exchange->doi->finalize_exchange (msg); + + /* No need for this anymore. */ + exchange_free (exchange); +} + +/* Stash a nonce into the exchange data. */ +static int +exchange_nonce (struct exchange *exchange, int peer, size_t nonce_sz, + u_int8_t *buf) +{ + int initiator = exchange->initiator ^ peer; + u_int8_t **nonce; + size_t *nonce_len; + char header[32]; + + nonce = initiator ? &exchange->nonce_i : &exchange->nonce_r; + nonce_len = initiator ? &exchange->nonce_i_len : &exchange->nonce_r_len; + *nonce_len = nonce_sz; + *nonce = malloc (nonce_sz); + if (!*nonce) + return -1; + memcpy (*nonce, buf, nonce_sz); + snprintf (header, 32, "exchange_nonce: NONCE_%c", initiator ? 'i' : 'r'); + log_debug_buf (LOG_MISC, 80, header, *nonce, nonce_sz); + return 0; +} + +/* Generate our NONCE. */ +int +exchange_gen_nonce (struct message *msg, size_t nonce_sz) +{ + struct exchange *exchange = msg->exchange; + u_int8_t *buf; + + buf = malloc (ISAKMP_NONCE_SZ + nonce_sz); + if (!buf) + return -1; + getrandom (buf + ISAKMP_NONCE_DATA_OFF, nonce_sz); + if (message_add_payload (msg, ISAKMP_PAYLOAD_NONCE, buf, + ISAKMP_NONCE_SZ + nonce_sz, 1)) + { + free (buf); + return -1; + } + return exchange_nonce (exchange, 0, nonce_sz, buf + ISAKMP_NONCE_DATA_OFF); +} + +/* Save the peer's NONCE. */ +int +exchange_save_nonce (struct message *msg) +{ + struct payload *noncep; + struct exchange *exchange = msg->exchange; + + noncep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NONCE]); + noncep->flags |= PL_MARK; + return exchange_nonce (exchange, 1, + GET_ISAKMP_GEN_LENGTH (noncep->p) + - ISAKMP_NONCE_DATA_OFF, + noncep->p + ISAKMP_NONCE_DATA_OFF); +} + +/* Save the peer's CERT REQuests. */ +int +exchange_save_certreq (struct message *msg) +{ + struct payload *cp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_CERT_REQ]); + struct exchange *exchange = msg->exchange; + struct certreq_aca *tmp; + + for ( ; cp; cp = TAILQ_NEXT (cp, link)) + { + cp->flags |= PL_MARK; + tmp = certreq_decode (GET_ISAKMP_CERTREQ_TYPE (cp->p), + cp->p + ISAKMP_CERTREQ_AUTHORITY_OFF, + GET_ISAKMP_GEN_LENGTH (cp->p) - + ISAKMP_CERTREQ_AUTHORITY_OFF); + if (tmp == NULL) + continue; + TAILQ_INSERT_TAIL (&exchange->aca_list, tmp, link); + } + + return 0; +} + +/* Free the list of pending CERTREQ */ + +void +exchange_free_aca_list (struct exchange *exchange) +{ + struct certreq_aca *aca; + + for (aca = TAILQ_FIRST (&exchange->aca_list); aca; + aca = TAILQ_FIRST (&exchange->aca_list)) + { + if (aca->data != NULL) + { + if (aca->handler != NULL) + aca->handler->free_aca (aca->data); + free (aca->data); + } + TAILQ_REMOVE (&exchange->aca_list, aca, link); + free (aca); + } +} + +/* Obtain Certificates from Acceptable Certification Authority */ + +int +exchange_add_certs (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct certreq_aca *aca; + u_int8_t *cert; + u_int32_t certlen; + + for (aca = TAILQ_FIRST (&exchange->aca_list); aca; + aca = TAILQ_NEXT (aca, link)) + { + /* XXX? If we can not satisfy a CERTREQ we drop the message */ + if (!aca->handler->cert_obtain (exchange, aca->data, &cert, &certlen)) + { + log_print ("exchange_add_certs: could not obtain cert for a type %d " + "cert request", aca->id); + return -1; + } + cert = realloc (cert, ISAKMP_CERT_SZ + certlen); + if (cert == NULL) + return -1; + memmove (cert + ISAKMP_CERT_DATA_OFF, cert, certlen); + SET_ISAKMP_CERT_ENCODING (cert, aca->id); + if (message_add_payload (msg, ISAKMP_PAYLOAD_CERT, cert, + ISAKMP_CERT_SZ + certlen, 1)) + { + free (cert); + return -1; + } + } + + /* We dont need the CERT REQs any more, they are anwsered */ + exchange_free_aca_list (exchange); + + return 0; +} diff --git a/sbin/isakmpd/exchange.h b/sbin/isakmpd/exchange.h new file mode 100644 index 00000000000..f84b0260944 --- /dev/null +++ b/sbin/isakmpd/exchange.h @@ -0,0 +1,165 @@ +/* $Id: exchange.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _EXCHANGE_H_ +#define _EXCHANGE_H_ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/queue.h> + +#include "exchange_num.h" +#include "isakmp.h" + +/* Remove an exchange if it has not been fully negotiated in this time. */ +#define EXCHANGE_MAX_TIME 120 + +struct crypto_xf; +struct certreq_aca; +struct doi; +struct event; +struct keystate; +struct message; +struct payload; +struct transport; +struct sa; + +struct exchange { + /* Link to exchanges with the same hash value. */ + LIST_ENTRY (exchange) link; + + /* When several SA's are being negotiated we keep them here. */ + TAILQ_HEAD (sa_head, sa) sa_list; + + /* + * The event that will occur when it has taken too long time to try to + * run the exchange and which will trigger auto-destruction. + */ + struct event *death; + + /* + * Both initiator and responder cookies. + * XXX For code clarity we might split this into two fields. + */ + u_int8_t cookies[ISAKMP_HDR_COOKIES_LEN]; + + /* The message ID signifying phase 2 exchanges. */ + u_int8_t message_id[ISAKMP_HDR_MESSAGE_ID_LEN]; + + /* The exchange type we are using. */ + u_int8_t type; + + /* Phase is 1 for ISAKMP SA exchanges, and 2 for application ones. */ + u_int8_t phase; + + /* The "step counter" of the exchange, starting from zero. */ + u_int8_t step; + + /* 1 if we are the initiator, 0 if we are the responder. */ + u_int8_t initiator; + + /* Various flags, look below for descriptions. */ + u_int32_t flags; + + /* The DOI that is to handle DOI-specific issues for this exchange. */ + struct doi *doi; + + /* + * A "program counter" into the script that validate message contents for + * this exchange. + */ + int16_t *exch_pc; + + /* The last message received, used for checking for duplicates. */ + struct message *last_received; + + /* The last message sent, to be acked when something new is received. */ + struct message *last_sent; + + /* + * Initiator's & responder's nonces respectively, with lengths. + * XXX Should this be in the DOI-specific parts instead? + */ + u_int8_t *nonce_i; + size_t nonce_i_len; + u_int8_t *nonce_r; + size_t nonce_r_len; + + /* XXX Do we want to save these in the exchange at all? */ + u_int8_t *id_i; + size_t id_i_len; + u_int8_t *id_r; + size_t id_r_len; + + /* Crypto info needed to encrypt/decrypt packets in this exchange. */ + struct crypto_xf *crypto; + int key_length; + struct keystate *keystate; + + /* Acceptable authorities for cert requests */ + TAILQ_HEAD (aca_head, certreq_aca) aca_list; + + /* DOI-specific opaque data. */ + void *data; +}; + +/* The flag bits. */ +#define EXCHANGE_FLAG_I_COMMITTED 1 +#define EXCHANGE_FLAG_HE_COMMITTED 2 +#define EXCHANGE_FLAG_COMMITTED (EXCHANGE_FLAG_I_COMMITTED \ + | EXCHANGE_FLAG_HE_COMMITTED) +#define EXCHANGE_FLAG_ENCRYPT 4 + +extern void exchange_finalize (struct message *); +extern void exchange_free (struct exchange *); +extern void exchange_establish_p1 (struct transport *, u_int8_t, u_int32_t, + void *); +extern void exchange_establish_p2 (struct sa *, u_int8_t, void *); +extern int exchange_gen_nonce (struct message *, size_t); +extern void exchange_init (void); +extern struct exchange *exchange_lookup (u_int8_t *, int); +extern struct exchange *exchange_lookup_from_icookie (u_int8_t *); +extern void exchange_report (void); +extern void exchange_run (struct message *); +extern int exchange_save_nonce (struct message *); +extern int exchange_save_certreq (struct message *); +extern void exchange_free_aca_list (struct exchange *); +extern int exchange_add_certs (struct message *); +extern u_int16_t *exchange_script (struct exchange *); +extern struct exchange *exchange_setup_p1 (struct message *, u_int32_t); +extern struct exchange *exchange_setup_p2 (struct message *, u_int8_t); +extern void exchange_upgrade_p1 (struct message *); + +#endif /* _EXCHANGE_H_ */ diff --git a/sbin/isakmpd/exchange_num.cst b/sbin/isakmpd/exchange_num.cst new file mode 100644 index 00000000000..821020e2cd9 --- /dev/null +++ b/sbin/isakmpd/exchange_num.cst @@ -0,0 +1,46 @@ +# $Id: exchange_num.cst,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +# Special exchange script symbols. +EXCHANGE_SCRIPT +# Special type signifying PAYLOAD_HASH or PALOAD_SIG must be present. + AUTH -1 +# Special type signifying PAYLOAD_NOTIFY or PALOAD_DELETE must be present. + INFO -2 +# Switch roles at this point in the exchange. + SWITCH -3 +# End of script + END -4 +. diff --git a/sbin/isakmpd/field.c b/sbin/isakmpd/field.c new file mode 100644 index 00000000000..d040886018b --- /dev/null +++ b/sbin/isakmpd/field.c @@ -0,0 +1,258 @@ +/* $Id: field.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "constants.h" +#include "field.h" +#include "log.h" +#include "util.h" + +static char *field_debug_raw (u_int8_t *, size_t, struct constant_map **); +static char *field_debug_num (u_int8_t *, size_t, struct constant_map **); +static char *field_debug_mask (u_int8_t *, size_t, struct constant_map **); +static char *field_debug_ign (u_int8_t *, size_t, struct constant_map **); +static char *field_debug_cst (u_int8_t *, size_t, struct constant_map **); + +/* Contents must match the enum in struct field. */ +static char *(*decode_field[]) (u_int8_t *, size_t, struct constant_map **) = { + field_debug_raw, + field_debug_num, + field_debug_mask, + field_debug_ign, + field_debug_cst +}; + +/* + * Return a string showing the hexadecimal contents of the LEN-sized buffer + * BUF. MAPS should be zero and is only here because the API requires it. + */ +static char * +field_debug_raw (u_int8_t *buf, size_t len, struct constant_map **maps) +{ + char *retval, *p; + + if (len == 0) + return 0; + retval = malloc (3 + len * 2); + if (!retval) + return 0; + strcpy (retval, "0x"); + p = retval + 2; + while (len--) + { + sprintf (p, "%02x", *buf++); + p += 2; + } + return retval; +} + +/* + * Convert the unsigned LEN-sized number at BUF of network byteorder to a + * 32-bit unsigned integer of host byteorder pointed to by VAL. + */ +static int +extract_val (u_int8_t *buf, size_t len, u_int32_t *val) +{ + switch (len) + { + case 1: + *val = *buf; + break; + case 2: + *val = decode_16 (buf); + break; + case 4: + *val = decode_32 (buf); + break; + default: + return -1; + } + return 0; +} + +/* + * Return a textual representation of the unsigned number pointed to by BUF + * which is LEN octets long. MAPS should be zero and is only here because + * the API requires it. + */ +static char * +field_debug_num (u_int8_t *buf, size_t len, struct constant_map **maps) +{ + char *retval; + u_int32_t val; + + if (extract_val (buf, len, &val)) + return 0; + asprintf (&retval, "%u", val); + return retval; +} + +/* + * Return the symbolic names of the flags pointed to by BUF which is LEN + * octets long, using the constant maps MAPS. + */ +static char * +field_debug_mask (u_int8_t *buf, size_t len, struct constant_map **maps) +{ + u_int32_t val; + u_int32_t bit; + char *retval, *new_buf, *name; + size_t buf_sz; + + if (extract_val (buf, len, &val)) + return 0; + + /* Size for brackets, two spaces and a NUL terminator. */ + buf_sz = 5; + retval = malloc (buf_sz); + if (!retval) + return 0; + + strcpy (retval, "[ "); + for (bit = 1; bit; bit <<= 1) + { + if (val & bit) + { + name = constant_name_maps (maps, bit); + buf_sz += strlen (name); + new_buf = realloc (retval, buf_sz); + if (!new_buf) + { + free (retval); + return 0; + } + retval = new_buf; + strcat (retval, name); + strcat (retval, " "); + } + } + strcat (retval, "]"); + return retval; +} + +/* + * Just a dummy needed to skip the unused LEN sized space at BUF. MAPS + * should be zero and is only here because the API requires it. + */ +static char * +field_debug_ign (u_int8_t *buf, size_t len, struct constant_map **maps) +{ + return 0; +} + +/* + * Return the symbolic name of a constant pointed to by BUF which is LEN + * octets long, using the constant maps MAPS. + */ +static char * +field_debug_cst (u_int8_t *buf, size_t len, struct constant_map **maps) +{ + u_int32_t val; + + if (extract_val (buf, len, &val)) + return 0; + + return strdup (constant_name_maps (maps, val)); +} + +/* Pretty-print a field from BUF as described by F. */ +void +field_dump_field (struct field *f, u_int8_t *buf) +{ + char *value; + + value = decode_field[(int)f->type] (buf + f->offset, f->len, f->maps); + if (value) + { + log_debug (LOG_MESSAGE, 70, "%s: %s", f->name, value); + free (value); + } +} + +/* Pretty-print all the fields of BUF as described in FIELDS. */ +void +field_dump_payload (struct field *fields, u_int8_t *buf) +{ + struct field *field; + + for (field = fields; field->name; field++) + field_dump_field (field, buf); +} + +/* Return the numeric value of the field F of BUF. */ +u_int32_t +field_get_num (struct field *f, u_int8_t *buf) +{ + u_int32_t val; + + if (extract_val(buf + f->offset, f->len, &val)) + return 0; + return val; +} + +/* Stash the number VAL into BUF's field F. */ +void +field_set_num (struct field *f, u_int8_t *buf, u_int32_t val) +{ + switch (f->len) + { + case 1: + buf[f->offset] = val; + break; + case 2: + encode_16 (buf + f->offset, val); + break; + case 4: + encode_32 (buf + f->offset, val); + break; + } +} + +/* Stash BUF's raw field F into VAL. */ +void +field_get_raw (struct field *f, u_int8_t *buf, u_int8_t *val) +{ + memcpy (val, buf + f->offset, f->len); +} + +/* Stash the buffer VAL into BUF's field F. */ +void +field_set_raw (struct field *f, u_int8_t *buf, u_int8_t *val) +{ + memcpy (buf + f->offset, val, f->len); +} diff --git a/sbin/isakmpd/field.h b/sbin/isakmpd/field.h new file mode 100644 index 00000000000..48943e3d07b --- /dev/null +++ b/sbin/isakmpd/field.h @@ -0,0 +1,56 @@ +/* $Id: field.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _FIELD_H_ +#define _FIELD_H_ + +#include <sys/types.h> + +struct field { + char *name; + int offset; + size_t len; + enum { raw, num, mask, ign, cst } type; + struct constant_map **maps; +}; + +extern void field_dump_field (struct field *, u_int8_t *); +extern void field_dump_payload (struct field *, u_int8_t *); +extern u_int32_t field_get_num (struct field *, u_int8_t *); +extern void field_get_raw (struct field *, u_int8_t *, u_int8_t *); +extern void field_set_num (struct field *, u_int8_t *, u_int32_t); +extern void field_set_raw (struct field *, u_int8_t *, u_int8_t *); + +#endif /* _FIELD_H_ */ diff --git a/sbin/isakmpd/genconstants.sh b/sbin/isakmpd/genconstants.sh new file mode 100644 index 00000000000..757b3274fe3 --- /dev/null +++ b/sbin/isakmpd/genconstants.sh @@ -0,0 +1,115 @@ +# $Id: genconstants.sh,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +base=`basename $1` +upcased_name=`echo $base |tr a-z A-Z` + +awk=${AWK:-awk} + +locase_function='function locase (str) { + cmd = "echo " str " |tr A-Z a-z" + cmd | getline retval; + close (cmd); + return retval; +}' + +$awk " +$locase_function +"' +BEGIN { + print "/* DO NOT EDIT-- this file is automatically generated. */\n" + print "#ifndef _'$upcased_name'_H_" + print "#define _'$upcased_name'_H_\n" + print "#include \"constants.h\"\n" +} + +/^[#.]/ { + next +} + +/^[^ ]/ { + prefix = $1 + printf ("extern struct constant_map %s_cst[];\n\n", locase(prefix)); + next +} + +/^[ ]/ && $1 { + printf ("#define %s_%s %s\n", prefix, $1, $2) + next +} + +{ + print +} + +END { + printf ("\n") + print "#endif /* _'$upcased_name'_H_ */" +} +' <$1.cst >$base.h + +$awk " +$locase_function +"' +BEGIN { + print "/* DO NOT EDIT-- this file is automatically generated. */\n" + print "#include \"constants.h\"\n" + print "#include \"'$base'.h\"\n" +} + +/^#/ { + next +} + +/^\./ { + print " { 0, 0 }\n};\n" + next +} + +/^[^ ]/ { + prefix = $1 + printf ("struct constant_map %s_cst[] = {\n", locase(prefix)) + next +} + +/^[ ]/ && $1 { + printf (" { %s_%s, \"%s\" }, \n", prefix, $1, $1) + next +} + +{ + print +} +' <$1.cst >$base.c diff --git a/sbin/isakmpd/genfields.sh b/sbin/isakmpd/genfields.sh new file mode 100644 index 00000000000..0cb6db41cd7 --- /dev/null +++ b/sbin/isakmpd/genfields.sh @@ -0,0 +1,187 @@ +# $Id: genfields.sh,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +base=`basename $1` +upcased_name=`echo $base |tr a-z A-Z` + +awk=${AWK:-awk} + +locase_function='function locase (str) { + cmd = "echo " str " |tr A-Z a-z" + cmd | getline retval; + close (cmd); + return retval; +}' + +$awk " +$locase_function +"' +BEGIN { + print "/* DO NOT EDIT-- this file is automatically generated. */\n" + print "#ifndef _'$upcased_name'_H_" + print "#define _'$upcased_name'_H_\n" + + print "#include \"field.h\"\n" + + print "struct constant_map;\n" +} + +/^#/ { + next +} + +/^\./ { + printf ("#define %s_SZ %d\n", prefix, off) + size[prefix] = off + next +} + +/^[^ ]/ { + prefix = $1 + printf ("extern struct field %s_fld[];\n\n", locase(prefix)); + if ($3) + { + off = size[$3] + } + else + { + off = 0 + } + i = 0 + next +} + +/^[ ]/ && $1 { + printf ("#define %s_%s_OFF %d\n", prefix, $1, off) + if ($3) + { + printf ("#define %s_%s_LEN %d\n", prefix, $1, $3) + } + if ($4) + { + printf ("extern struct constant_map *%s_%s_maps[];\n", locase(prefix), + locase($1)) + } + if ($2 == "raw") + { + printf ("#define GET_%s_%s(buf, val) ", prefix, $1) + printf ("field_get_raw (%s_fld + %d, buf, val)\n", locase(prefix), i) + printf ("#define SET_%s_%s(buf, val) ", prefix, $1) + printf ("field_set_raw (%s_fld + %d, buf, val)\n", locase(prefix), i) + } + else + { + printf ("#define GET_%s_%s(buf) field_get_num (%s_fld + %d, buf)\n", + prefix, $1, locase(prefix), i) + printf ("#define SET_%s_%s(buf, val) ", prefix, $1) + printf ("field_set_num (%s_fld + %d, buf, val)\n", locase(prefix), i) + } + off += $3 + i++ + next +} + +{ + print +} + +END { + printf ("\n") + print "#endif /* _'$upcased_name'_H_ */" +} +' <$1.fld >$base.h + +$awk " +$locase_function +"' +BEGIN { + print "/* DO NOT EDIT-- this file is automatically generated. */\n" + print "#include \"constants.h\"" + print "#include \"field.h\"" + print "#include \"'$base'.h\"" + print "#include \"isakmp_num.h\"" + print "#include \"ipsec_num.h\"" +} + +/^#/ { + next +} + +/^\./ { + print " { 0, 0, 0, 0, 0 }\n};\n" + size[prefix] = off + for (map in maps) + { + printf ("struct constant_map *%s_%s_maps[] = { ", locase(prefix), + locase(map)) + printf ("%s,0 };\n", maps[map]) + } + next +} + +/^[^ ]/ { + prefix = $1 + printf ("struct field %s_fld[] = {\n", locase(prefix)) + if ($3) + { + off = size[$3] + } + else + { + off = 0 + } + delete maps + next +} + +/^[ ]/ && $1 { + if ($4) + { + maps_name = locase(prefix)"_"locase($1)"_maps" + maps[$1] = $4 + } + else + { + maps_name = "0" + } + printf (" { \"%s\", %d, %d, %s, %s }, \n", $1, off, $3, $2, maps_name) + off += $3 + next +} + +{ + print +} +' <$1.fld >$base.c diff --git a/sbin/isakmpd/gmp_util.c b/sbin/isakmpd/gmp_util.c new file mode 100644 index 00000000000..c3f0fc32601 --- /dev/null +++ b/sbin/isakmpd/gmp_util.c @@ -0,0 +1,76 @@ +/* $Id: gmp_util.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <gmp.h> + +#include "gmp_util.h" + +/* Various utility functions for gmp, used in more than one module */ + +u_int32_t +mpz_sizeinoctets (mpz_ptr a) +{ + return (7 + mpz_sizeinbase (a, 2)) >> 3; +} + +void +mpz_getraw (u_int8_t *raw, mpz_ptr v, u_int32_t len) +{ + mpz_t a, tmp; + + mpz_init_set (a, v); + mpz_init (tmp); + + while (len-- > 0) + raw[len] = mpz_fdiv_qr_ui (a, tmp, a, 256); + + mpz_clear (a); + mpz_clear (tmp); +} + +void +mpz_setraw (mpz_ptr d, u_int8_t *s, u_int32_t l) +{ + u_int32_t i; + + mpz_set_ui (d, 0); + for (i = 0; i < l; i++) + { + mpz_mul_ui (d, d, 256); + mpz_add_ui (d, d, s[i]); + } +} + diff --git a/sbin/isakmpd/gmp_util.h b/sbin/isakmpd/gmp_util.h new file mode 100644 index 00000000000..a600c448ffc --- /dev/null +++ b/sbin/isakmpd/gmp_util.h @@ -0,0 +1,43 @@ +/* $Id: gmp_util.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _GMP_UTIL_H_ +#define _GMP_UTIL_H_ + +u_int32_t mpz_sizeinoctets (mpz_ptr); +void mpz_getraw (u_int8_t *, mpz_ptr, u_int32_t); +void mpz_setraw (mpz_ptr, u_int8_t *, u_int32_t); + +#endif /* _GMP_UTIL_H_ */ diff --git a/sbin/isakmpd/hash.c b/sbin/isakmpd/hash.c new file mode 100644 index 00000000000..d78e941427b --- /dev/null +++ b/sbin/isakmpd/hash.c @@ -0,0 +1,135 @@ +/* $Id: hash.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <string.h> +#include <md5.h> +#include <sha1.h> + +#include "hash.h" + +void hmac_init (struct hash *, unsigned char *, int); +void hmac_final (unsigned char *, struct hash *); + +/* Temporary hash contexts. */ +static union { + MD5_CTX md5ctx; + SHA1_CTX sha1ctx; +} Ctx, Ctx2; + +/* Temporary hash digest. */ +static unsigned char digest[HASH_MAX]; + +/* Encapsulation of hash functions. */ + +static struct hash hashes[] = { + { HASH_MD5, 5, MD5_SIZE, (void *)&Ctx.md5ctx, digest, + sizeof (MD5_CTX), (void *)&Ctx2.md5ctx, + (void (*) (void *))MD5Init, + (void (*) (void *, unsigned char *, unsigned int))MD5Update, + (void (*) (unsigned char *, void *))MD5Final, + hmac_init, hmac_final }, + { HASH_SHA1, 6, SHA1_SIZE, (void *)&Ctx.sha1ctx, digest, + sizeof (SHA1_CTX), (void *)&Ctx2.sha1ctx, + (void (*) (void *))SHA1Init, + (void (*) (void *, unsigned char *, unsigned int))SHA1Update, + (void (*) (unsigned char *, void *))SHA1Final, + hmac_init, hmac_final }, +}; + +struct hash * +hash_get (enum hashes hashtype) +{ + int i; + + for (i = 0; i < sizeof hashes / sizeof hashes[0]; i++) + if (hashtype == hashes[i].type) + return &hashes[i]; + + return NULL; +} + +/* + * Initial a hash for HMAC usage this requires a special init function. + * ctx, ctx2 hold the contexts, if you want to use the hash object for + * something else in the meantime, be sure to store the contexts somewhere. + */ + +void +hmac_init (struct hash *hash, unsigned char *okey, int len) +{ + int i, blocklen = HMAC_BLOCKLEN; + unsigned char key[HMAC_BLOCKLEN]; + + if (len > blocklen) + { + /* Truncate key down to blocklen */ + hash->Init (hash->ctx); + hash->Update (hash->ctx, okey, len); + hash->Final (key, hash->ctx); + } + else + { + memset (key, 0, blocklen); + memcpy (key, okey, len); + } + + /* HMAC I and O pad computation */ + for (i=0; i < blocklen; i++) + key[i] ^= HMAC_IPAD_VAL; + + hash->Init (hash->ctx); + hash->Update (hash->ctx, key, blocklen); + + for (i=0; i < blocklen; i++) + key[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); + + hash->Init (hash->ctx2); + hash->Update (hash->ctx2, key, blocklen); + + memset (key, 0, blocklen); +} + +/* + * HMAC Final function + */ + +void +hmac_final (unsigned char *digest, struct hash *hash) +{ + hash->Final (digest, hash->ctx); + hash->Update (hash->ctx2, digest, hash->hashsize); + hash->Final (digest, hash->ctx2); +} diff --git a/sbin/isakmpd/hash.h b/sbin/isakmpd/hash.h new file mode 100644 index 00000000000..2bd8c4ee046 --- /dev/null +++ b/sbin/isakmpd/hash.h @@ -0,0 +1,74 @@ +/* $Id: hash.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _HASH_H_ +#define _HASH_H_ + +/* Normal mode hash encapsulation */ + +#define MD5_SIZE 16 +#define SHA1_SIZE 20 +#define HASH_MAX SHA1_SIZE + +enum hashes { + HASH_MD5 = 0, + HASH_SHA1 +}; + +struct hash { + enum hashes type; + int id; /* ISAKMP/Oakley ID */ + u_int8_t hashsize; /* Size of the hash */ + void *ctx; /* Pointer to a context, for HMAC ictx */ + char *digest; /* Pointer to a digest */ + int ctxsize; + void *ctx2; /* Pointer to a 2nd context, for HMAC octx */ + void (*Init) (void *); + void (*Update) (void *, unsigned char *, unsigned int); + void (*Final) (unsigned char *, void *); + void (*HMACInit) (struct hash *, unsigned char *, int); + void (*HMACFinal) (unsigned char *, struct hash *); +}; + +/* HMAC Hash Encapsulation */ + +#define HMAC_IPAD_VAL 0x36 +#define HMAC_OPAD_VAL 0x5C +#define HMAC_BLOCKLEN 64 + +extern struct hash *hash_get (enum hashes); +extern void hmac_init (struct hash *, unsigned char *, int); + +#endif /* _HASH_H_ */ diff --git a/sbin/isakmpd/if.c b/sbin/isakmpd/if.c new file mode 100644 index 00000000000..2be69a14928 --- /dev/null +++ b/sbin/isakmpd/if.c @@ -0,0 +1,121 @@ +/* $Id: if.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <stdlib.h> +#include <unistd.h> + +#include "if.h" + +/* XXX Unsafe if either x or y has side-effects. */ +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +/* Most boxes has less than 16 interfaces, so this might be a good guess. */ +#define INITIAL_IFREQ_COUNT 16 + +/* + * Get all network interface configurations. + * Return 0 if successful, -1 otherwise. + */ +int +siocgifconf (struct ifconf *ifcp) +{ + int s; + int len; + caddr_t buf, new_buf; + + /* Get a socket to ask for the network interface configurations. */ + s = socket (AF_INET, SOCK_DGRAM, 0); + if (s == -1) + return -1; + + len = sizeof (struct ifreq) * INITIAL_IFREQ_COUNT; + buf = 0; + while (1) + { + /* + * Allocate a larger buffer each time around the loop and get the + * network interfaces configurations into it. + */ + ifcp->ifc_len = len; + new_buf = realloc (buf, len); + if (!new_buf) + goto err; + ifcp->ifc_buf = buf = new_buf; + if (ioctl (s, SIOCGIFCONF, ifcp) == -1) + goto err; + + /* + * If there is place for another ifreq we can be sure that the buffer + * was big enough, otherwise double the size and try again. + */ + if (len - ifcp->ifc_len >= sizeof (struct ifreq)) + break; + len *= 2; + } + close (s); + return 0; + +err: + if (buf) + free (buf); + close (s); + return -1; +} + +int +if_map (void (*func) (struct ifreq *, void *), void *arg) +{ + struct ifconf ifc; + struct ifreq *ifrp; + caddr_t limit, p; + size_t len; + + if (siocgifconf (&ifc)) + return -1; + + limit = ifc.ifc_buf + ifc.ifc_len; + for (p = ifc.ifc_buf; p < limit; p += len) + { + ifrp = (struct ifreq *)p; + (*func) (ifrp, arg); + len = sizeof ifrp->ifr_name + + MAX (ifrp->ifr_addr.sa_len, sizeof ifrp->ifr_addr); + } + return 0; +} diff --git a/sbin/isakmpd/if.h b/sbin/isakmpd/if.h new file mode 100644 index 00000000000..21d52600c31 --- /dev/null +++ b/sbin/isakmpd/if.h @@ -0,0 +1,47 @@ +/* $Id: if.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _IF_H_ +#define _IF_H_ + +#include <sys/types.h> + +struct ifreq; +struct ifconf; + +extern int if_map (void (*) (struct ifreq *, void *), void *); +extern int siocgifconf (struct ifconf *); + +#endif /* _IF_H_ */ diff --git a/sbin/isakmpd/ike_auth.c b/sbin/isakmpd/ike_auth.c new file mode 100644 index 00000000000..a109e92a199 --- /dev/null +++ b/sbin/isakmpd/ike_auth.c @@ -0,0 +1,539 @@ +/* $Id: ike_auth.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#include "asn.h" +#include "cert.h" +#include "conf.h" +#include "exchange.h" +#include "gmp.h" +#include "gmp_util.h" +#include "hash.h" +#include "ike_auth.h" +#include "ipsec.h" +#include "ipsec_doi.h" +#include "log.h" +#include "message.h" +#include "pkcs.h" +#include "prf.h" + +static u_int8_t *enc_gen_skeyid (struct exchange *, size_t *); +static u_int8_t *pre_shared_gen_skeyid (struct exchange *, size_t *); +static u_int8_t *sig_gen_skeyid (struct exchange *, size_t *); + +static int pre_shared_decode_hash (struct message *); +static int rsa_sig_decode_hash (struct message *); +static int pre_shared_encode_hash (struct message *); +static int rsa_sig_encode_hash (struct message *); + +static int ike_auth_hash (struct exchange *, u_int8_t *); + +static struct ike_auth ike_auth[] = { + { IKE_AUTH_PRE_SHARED, pre_shared_gen_skeyid, pre_shared_decode_hash, + pre_shared_encode_hash}, + { IKE_AUTH_DSS, sig_gen_skeyid, pre_shared_decode_hash, + pre_shared_encode_hash}, + { IKE_AUTH_RSA_SIG, sig_gen_skeyid, rsa_sig_decode_hash, + rsa_sig_encode_hash}, + { IKE_AUTH_RSA_ENC, enc_gen_skeyid, pre_shared_decode_hash, + pre_shared_encode_hash}, + { IKE_AUTH_RSA_ENC_REV, enc_gen_skeyid, pre_shared_decode_hash, + pre_shared_encode_hash}, +}; + +struct ike_auth * +ike_auth_get (u_int16_t id) +{ + int i; + + for (i = 0; i < sizeof ike_auth / sizeof ike_auth[0]; i++) + if (id == ike_auth[i].id) + return &ike_auth[i]; + return 0; +} + +static u_int8_t * +pre_shared_gen_skeyid (struct exchange *exchange, size_t *sz) +{ + struct prf *prf; + struct ipsec_exch *ie = exchange->data; + u_int8_t *skeyid; + u_int8_t *key; + + /* + * Get the default pre-shared key. + * XXX This will be per-IP configurable too later, and representable in + * hex too. + */ + key = conf_get_str ("pre_shared", "key"); + prf = prf_alloc (ie->prf_type, ie->hash->type, key, strlen (key)); + if (!prf) + return 0; + + *sz = prf->blocksize; + skeyid = malloc (*sz); + if (!skeyid) + { + prf_free (prf); + return 0; + } + + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + prf->Update (prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); + prf->Final (skeyid, prf->prfctx); + prf_free (prf); + + return skeyid; +} + +/* Both DSS & RSA signature authentication uses this algorithm. */ +static u_int8_t * +sig_gen_skeyid (struct exchange *exchange, size_t *sz) +{ + struct prf *prf; + struct ipsec_exch *ie = exchange->data; + u_int8_t *skeyid, *key; + + key = malloc (exchange->nonce_i_len + exchange->nonce_r_len); + if (!key) + return 0; + memcpy (key, exchange->nonce_i, exchange->nonce_i_len); + memcpy (key + exchange->nonce_i_len, exchange->nonce_r, + exchange->nonce_r_len); + prf = prf_alloc (ie->prf_type, ie->hash->type, key, + exchange->nonce_i_len + exchange->nonce_r_len); + free (key); + if (!prf) + return 0; + + *sz = prf->blocksize; + skeyid = malloc (*sz); + if (!skeyid) + { + prf_free (prf); + return 0; + } + + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); + prf->Final (skeyid, prf->prfctx); + prf_free (prf); + + return skeyid; +} + +/* + * Both standard and revised RSA encryption authentication uses this SKEYID + * computation. + */ +static u_int8_t * +enc_gen_skeyid (struct exchange *exchange, size_t *sz) +{ + struct prf *prf; + struct ipsec_exch *ie = exchange->data; + struct hash *hash = ie->hash; + u_int8_t *skeyid; + + hash->Init (hash->ctx); + hash->Update (hash->ctx, exchange->nonce_i, exchange->nonce_i_len); + hash->Update (hash->ctx, exchange->nonce_r, exchange->nonce_r_len); + hash->Final (hash->digest, hash->ctx); + prf = prf_alloc (ie->prf_type, hash->type, hash->digest, *sz); + if (!prf) + return 0; + + *sz = prf->blocksize; + skeyid = malloc (*sz); + if (!skeyid) + { + prf_free (prf); + return 0; + } + + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + prf->Final (skeyid, prf->prfctx); + prf_free (prf); + + return skeyid; +} + +static int +pre_shared_decode_hash (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct payload *payload; + size_t hashsize = ie->hash->hashsize; + char header[80]; + int initiator = exchange->initiator; + u_int8_t **hash_p; + + /* Choose the right fields to fill-in. */ + hash_p = initiator ? &ie->hash_r : &ie->hash_i; + + payload = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + if (!payload) + return -1; + + /* Check that the hash is of the correct size. */ + if (GET_ISAKMP_GEN_LENGTH (payload->p) - ISAKMP_GEN_SZ != hashsize) + return -1; + + /* XXX Need this hash be in the SA? */ + *hash_p = malloc (hashsize); + if (!*hash_p) + return -1; + + memcpy (*hash_p, payload->p + ISAKMP_HASH_DATA_OFF, hashsize); + snprintf (header, 80, "pre_shared_decode_hash: HASH_%c", + initiator ? 'R' : 'I'); + log_debug_buf (LOG_MISC, 80, header, *hash_p, hashsize); + + payload->flags |= PL_MARK; + + return 0; +} + +/* + * Decrypt the HASH in SIG, we already need a parsed ID payload + */ + +static int +rsa_sig_decode_hash (struct message *msg) +{ + struct cert_handler *handler; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct payload *p; + struct rsa_public_key key; + size_t hashsize = ie->hash->hashsize; + char header[80]; + int initiator = exchange->initiator; + u_int8_t **hash_p, *id_cert, *id; + u_int16_t len; + u_int32_t id_cert_len; + size_t id_len; + + /* Choose the right fields to fill-in. */ + hash_p = initiator ? &ie->hash_r : &ie->hash_i; + id = initiator ? exchange->id_r : exchange->id_i; + id_len = initiator ? exchange->id_r_len : exchange->id_i_len; + + if (id == NULL || id_len == 0) + { + log_print ("rsa_sig_decode_hash: no ID in sa"); + return -1; + } + + /* Just bother with the ID data field. */ + id += ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ; + id_len -= ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ; + + p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_CERT]); + if (!p) + return -1; + + if ((handler = cert_get (GET_ISAKMP_CERT_ENCODING(p->p))) == NULL) + { + log_print ("rsa_sig_decode_hash: no handler for CERT encoding"); + return -1; + } + + /* XXX - this assumes IPv4 here */ + if (!handler->cert_get_subject (p->p + ISAKMP_CERT_DATA_OFF, + GET_ISAKMP_GEN_LENGTH(p->p) - + ISAKMP_CERT_DATA_OFF, + &id_cert, &id_cert_len)) + { + log_print ("rsa_sig_decode_hash: can not get subject from CERT"); + return -1; + } + + if (id_cert_len != id_len || memcmp (id, id_cert, id_len)) + { + log_print ("rsa_sig_decode_hash: CERT subject does not match ID"); + free (id_cert); + return -1; + } + free (id_cert); + + if (!handler->cert_get_key (p->p + ISAKMP_CERT_DATA_OFF, + GET_ISAKMP_GEN_LENGTH(p->p) - + ISAKMP_CERT_DATA_OFF, + &key)) + { + log_print ("rsa_sig_decode_hash: decoding payload CERT failed"); + return -1; + } + + p->flags |= PL_MARK; + + p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SIG]); + if (!p) + { + pkcs_free_public_key (&key); + return -1; + } + + /* Check that the sig is of the correct size. */ + if (GET_ISAKMP_GEN_LENGTH (p->p) - ISAKMP_SIG_SZ != mpz_sizeinoctets (key.n)) + { + pkcs_free_public_key (&key); + log_print ("rsa_sig_decode_hash: SIG payload length does not match " + "public key"); + return -1; + } + + if (!pkcs_rsa_decrypt (PKCS_PRIVATE, key.n, key.e, + p->p + ISAKMP_SIG_DATA_OFF, hash_p, &len)) + { + pkcs_free_public_key (&key); + return -1; + } + + pkcs_free_public_key (&key); + + if (len != hashsize) + { + free (*hash_p); + *hash_p = NULL; + return -1; + } + + snprintf (header, 80, "rsa_sig_decode_hash: HASH_%c", initiator ? 'R' : 'I'); + log_debug_buf (LOG_MISC, 80, header, *hash_p, hashsize); + + p->flags |= PL_MARK; + + return 0; +} + +static int +pre_shared_encode_hash (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + size_t hashsize = ie->hash->hashsize; + char header[80]; + int initiator = exchange->initiator; + u_int8_t *buf; + + /* XXX - hashsize is not necessarily prf->blocksize */ + buf = malloc (ISAKMP_HASH_SZ + hashsize); + if (!buf) + { + /* XXX Log? */ + return -1; + } + + if (ike_auth_hash (exchange, buf + ISAKMP_HASH_DATA_OFF) == -1) + { + /* XXX Log? */ + free (buf); + return -1; + } + + snprintf (header, 80, "pre_shared_encode_hash: HASH_%c", + initiator ? 'I' : 'R'); + log_debug_buf (LOG_MISC, 80, header, buf + ISAKMP_HASH_DATA_OFF, hashsize); + if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, buf, + ISAKMP_HASH_SZ + hashsize, 1)) + { + /* XXX Log? */ + free (buf); + return -1; + } + + return 0; +} + + +/* Encrypt the HASH into a SIG type */ + +static int +rsa_sig_encode_hash (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + size_t hashsize = ie->hash->hashsize; + struct cert_handler *handler; + struct rsa_private_key key; + char header[80]; + int initiator = exchange->initiator; + u_int8_t *buf, *asn, *data; + u_int32_t asnlen, datalen; + char *keyfile; + + /* XXX - this needs to be configureable */ + handler = cert_get (ISAKMP_CERTENC_X509_SIG); + if (handler == NULL) + { + /* XXX - Log? */ + return -1; + } + /* XXX - implicitly uses exchange->id_{i,r} */ + if (!handler->cert_obtain (exchange, NULL, &data, &datalen)) + { + /* XXX - Log? */ + return -1; + } + + buf = realloc (data, ISAKMP_CERT_SZ + datalen); + if (buf == NULL) + { + /* XXX Log? */ + free (data); + return -1; + } + memmove (buf + ISAKMP_CERT_SZ, buf, datalen); + SET_ISAKMP_CERT_ENCODING (buf, ISAKMP_CERTENC_X509_SIG); + if (message_add_payload (msg, ISAKMP_PAYLOAD_CERT, buf, + ISAKMP_CERT_SZ + datalen, 1)) + { + /* XXX Log? */ + free (buf); + return -1; + } + + /* XXX - do we want to store our files in ASN.1 ? */ + keyfile = conf_get_str ("rsa_sig", "privkey"); + if (!asn_get_from_file (keyfile, &asn, &asnlen)) + { + /* XXX Log? */ + return -1; + } + + if (!pkcs_private_key_from_asn (&key, asn, asnlen)) + { + /* XXX Log? */ + free (asn); + return -1; + } + free (asn); + + /* XXX - hashsize is not necessarily prf->blocksize */ + buf = malloc (hashsize); + if (!buf) + { + /* XXX Log? */ + pkcs_free_private_key (&key); + return -1; + } + + if (ike_auth_hash (exchange, buf) == -1) + { + /* XXX Log? */ + free (buf); + pkcs_free_private_key (&key); + return -1; + } + + snprintf (header, 80, "rsa_sig_encode_hash: HASH_%c", initiator ? 'I' : 'R'); + log_debug_buf (LOG_MISC, 80, header, buf, hashsize); + + if (!pkcs_rsa_encrypt (PKCS_PRIVATE, key.n, key.e, buf, hashsize, + &data, &datalen)) + { + free (buf); + pkcs_free_private_key (&key); + return -1; + } + pkcs_free_private_key (&key); + free (buf); + + buf = realloc (data, ISAKMP_SIG_SZ + datalen); + if (!buf) + { + /* XXX Log? */ + free (data); + return -1; + } + memmove (buf + ISAKMP_SIG_SZ, buf, datalen); + + snprintf (header, 80, "rsa_sig_encode_hash: SIG_%c", initiator ? 'I' : 'R'); + log_debug_buf (LOG_MISC, 80, header, buf + ISAKMP_SIG_DATA_OFF, datalen); + if (message_add_payload (msg, ISAKMP_PAYLOAD_SIG, buf, + ISAKMP_SIG_SZ + datalen, 1)) + { + /* XXX Log? */ + free (buf); + return -1; + } + + return 0; +} + +int +ike_auth_hash (struct exchange *exchange, u_int8_t *buf) +{ + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + struct hash *hash = ie->hash; + int initiator = exchange->initiator; + u_int8_t *id; + size_t id_len; + + /* Choose the right fields to fill-in. */ + id = initiator ? exchange->id_i : exchange->id_r; + id_len = initiator ? exchange->id_i_len : exchange->id_r_len; + + /* Allocate the prf and start calculating our HASH. */ + prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); + if (!prf) + return -1; + + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len); + prf->Update (prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len); + prf->Update (prf->prfctx, + exchange->cookies + + (initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF), + ISAKMP_HDR_ICOOKIE_LEN); + prf->Update (prf->prfctx, + exchange->cookies + + (initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF), + ISAKMP_HDR_ICOOKIE_LEN); + prf->Update (prf->prfctx, ie->sa_i_b, ie->sa_i_b_len); + prf->Update (prf->prfctx, id, id_len); + prf->Final (buf, prf->prfctx); + prf_free (prf); + + return 0; +} diff --git a/sbin/isakmpd/ike_auth.h b/sbin/isakmpd/ike_auth.h new file mode 100644 index 00000000000..79224ff5b09 --- /dev/null +++ b/sbin/isakmpd/ike_auth.h @@ -0,0 +1,52 @@ +/* $Id: ike_auth.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _IKE_AUTH_H_ +#define _IKE_AUTH_H_ + +#include <sys/types.h> + +struct exchange; + +struct ike_auth { + u_int16_t id; + u_int8_t *(*gen_skeyid) (struct exchange *, size_t *); + int (*decode_hash) (struct message *); + int (*encode_hash) (struct message *); +}; + +extern struct ike_auth *ike_auth_get (u_int16_t); + +#endif /* _IKE_AUTH_H_ */ diff --git a/sbin/isakmpd/ike_main_mode.c b/sbin/isakmpd/ike_main_mode.c new file mode 100644 index 00000000000..94e8eddac7b --- /dev/null +++ b/sbin/isakmpd/ike_main_mode.c @@ -0,0 +1,883 @@ +/* $Id: ike_main_mode.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include "attribute.h" +#include "conf.h" +#include "constants.h" +#include "crypto.h" +#include "dh.h" +#include "doi.h" +#include "exchange.h" +#include "hash.h" +#include "ike_auth.h" +#include "ike_main_mode.h" +#include "ipsec.h" +#include "ipsec_doi.h" +#include "isakmp.h" +#include "log.h" +#include "math_group.h" +#include "message.h" +#include "prf.h" +#include "sa.h" +#include "transport.h" +#include "util.h" + +static int initiator_send_SA (struct message *); +static int initiator_recv_SA (struct message *); +static int initiator_send_KE_NONCE (struct message *); +static int initiator_recv_KE_NONCE (struct message *); +static int initiator_send_ID_AUTH (struct message *); +static int initiator_recv_ID_AUTH (struct message *); +static int responder_recv_SA (struct message *); +static int responder_send_SA (struct message *); +static int responder_recv_KE_NONCE (struct message *); +static int responder_send_KE_NONCE (struct message *); +static int responder_recv_ID_AUTH (struct message *); +static int responder_send_ID_AUTH (struct message *); +static int recv_KE_NONCE (struct message *); +static int recv_ID_AUTH (struct message *); +static int send_KE_NONCE (struct message *, size_t); +static int send_ID_AUTH (struct message *); +static int post_exchange_KE_NONCE (struct message *); + +int (*ike_main_mode_initiator[]) (struct message *) = { + initiator_send_SA, + initiator_recv_SA, + initiator_send_KE_NONCE, + initiator_recv_KE_NONCE, + initiator_send_ID_AUTH, + initiator_recv_ID_AUTH +}; + +int (*ike_main_mode_responder[]) (struct message *) = { + responder_recv_SA, + responder_send_SA, + responder_recv_KE_NONCE, + responder_send_KE_NONCE, + responder_recv_ID_AUTH, + responder_send_ID_AUTH +}; + +/* Offer a set of transforms to the responder. */ +static int +initiator_send_SA (struct message *msg) +{ + struct ipsec_exch *ie = msg->exchange->data; + u_int8_t *proposal = 0, *sa_buf = 0, *attr; + u_int8_t **transform = 0; + size_t transforms_len = 0, proposal_len, sa_len; + size_t *transform_len = 0; + struct conf_list *conf, *life_conf; + struct conf_list_node *xf, *life; + int i, value, update_nextp; + struct payload *p; + struct proto *proto; + + /* Get the list of transforms. */ + conf = conf_get_list ("Main mode initiator", "Offered-transforms"); + if (!conf) + return -1; + + transform = calloc (conf->cnt, sizeof *transform); + if (!transform) + goto bail_out; + transform_len = calloc (conf->cnt, sizeof *transform_len); + if (!transform_len) + goto bail_out; + + for (xf = TAILQ_FIRST (&conf->fields), i = 0; i < conf->cnt; + i++, xf = TAILQ_NEXT (xf, link)) + { + /* XXX The sizing needs to be dynamic. */ + transform[i] + = malloc (ISAKMP_TRANSFORM_SA_ATTRS_OFF + 16 * ISAKMP_ATTR_VALUE_OFF); + if (!transform[i]) + goto bail_out; + SET_ISAKMP_TRANSFORM_NO (transform[i], i); + SET_ISAKMP_TRANSFORM_ID (transform[i], IPSEC_TRANSFORM_KEY_IKE); + SET_ISAKMP_TRANSFORM_RESERVED (transform[i], 0); + + attr = transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; + + if (attribute_set_constant (xf->field, "ENCRYPTION_ALGORITHM", + ike_encrypt_cst, + IKE_ATTR_ENCRYPTION_ALGORITHM, &attr)) + goto bail_out; + + if (attribute_set_constant (xf->field, "HASH_ALGORITHM", ike_hash_cst, + IKE_ATTR_HASH_ALGORITHM, &attr)) + goto bail_out; + + if (attribute_set_constant (xf->field, "AUTHENTICATION_METHOD", + ike_auth_cst, IKE_ATTR_AUTHENTICATION_METHOD, + &attr)) + goto bail_out; + + if (attribute_set_constant (xf->field, "GROUP_DESCRIPTION", + ike_group_desc_cst, + IKE_ATTR_GROUP_DESCRIPTION, &attr)) + { + /* + * If no group description exists, try looking for a user-defined + * one. + */ + if (attribute_set_constant (xf->field, "GROUP_TYPE", ike_group_cst, + IKE_ATTR_GROUP_TYPE, &attr)) + goto bail_out; + +#if 0 + if (attribute_set_bignum (xf->field, "GROUP_PRIME", + IKE_ATTR_GROUP_PRIME, &attr)) + goto bail_out; + + if (attribute_set_bignum (xf->field, "GROUP_GENERATOR_2", + IKE_ATTR_GROUP_GENERATOR_2, &attr)) + goto bail_out; + + if (attribute_set_bignum (xf->field, "GROUP_GENERATOR_2", + IKE_ATTR_GROUP_GENERATOR_2, &attr)) + goto bail_out; + + if (attribute_set_bignum (xf->field, "GROUP_CURVE_A", + IKE_ATTR_GROUP_CURVE_A, &attr)) + goto bail_out; + + if (attribute_set_bignum (xf->field, "GROUP_CURVE_B", + IKE_ATTR_GROUP_CURVE_B, &attr)) + goto bail_out; +#endif + } + + /* + * Life durations are special, we should be able to specify + * several, one per type. + */ + life_conf = conf_get_list (xf->field, "Life"); + if (life_conf) + { + for (life = TAILQ_FIRST (&life_conf->fields); life; + life = TAILQ_NEXT (life, link)) + { + attribute_set_constant (life->field, "LIFE_TYPE", + ike_duration_cst, IKE_ATTR_LIFE_TYPE, + &attr); + + /* XXX Does only handle 16-bit entities! */ + value = conf_get_num (life->field, "LIFE_DURATION"); + if (value) + attr + = attribute_set_basic (attr, IKE_ATTR_LIFE_DURATION, value); + } + } + + attribute_set_constant (xf->field, "PRF", ike_prf_cst, IKE_ATTR_PRF, + &attr); + + value = conf_get_num (xf->field, "KEY_LENGTH"); + if (value) + attr = attribute_set_basic (attr, IKE_ATTR_KEY_LENGTH, value); + + value = conf_get_num (xf->field, "FIELD_SIZE"); + if (value) + attr = attribute_set_basic (attr, IKE_ATTR_FIELD_SIZE, value); + + value = conf_get_num (xf->field, "GROUP_ORDER"); + if (value) + attr = attribute_set_basic (attr, IKE_ATTR_GROUP_ORDER, value); + + /* Record the real transform size. */ + transforms_len += transform_len[i] = attr - transform[i]; + } + + proposal_len = ISAKMP_PROP_SPI_OFF; + proposal = malloc (proposal_len); + if (!proposal) + goto bail_out; + SET_ISAKMP_PROP_NO (proposal, 1); + SET_ISAKMP_PROP_PROTO (proposal, ISAKMP_PROTO_ISAKMP); + SET_ISAKMP_PROP_SPI_SZ (proposal, 0); + SET_ISAKMP_PROP_NTRANSFORMS (proposal, conf->cnt); + + /* XXX I would like to see this factored out. */ + proto = calloc (1, sizeof *proto); + if (!proto) + goto bail_out; + proto->no = 1; + proto->proto = ISAKMP_PROTO_ISAKMP; + proto->sa = TAILQ_FIRST (&msg->exchange->sa_list); + TAILQ_INSERT_TAIL (&TAILQ_FIRST (&msg->exchange->sa_list)->protos, proto, + link); + + sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; + sa_buf = malloc (sa_len); + if (!sa_buf) + goto bail_out; + SET_ISAKMP_SA_DOI (sa_buf, IPSEC_DOI_IPSEC); + SET_IPSEC_SIT_SIT (sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); + + /* + * Add the payloads. As this is a SA, we need to recompute the + * lengths of the payloads containing others. + */ + if (message_add_payload (msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) + goto bail_out; + SET_ISAKMP_GEN_LENGTH (sa_buf, + sa_len + proposal_len + transforms_len); + sa_buf = 0; + + if (message_add_payload (msg, ISAKMP_PAYLOAD_PROPOSAL, proposal, + proposal_len, 0)) + goto bail_out; + SET_ISAKMP_GEN_LENGTH (proposal, proposal_len + transforms_len); + proposal = 0; + + update_nextp = 0; + for (i = 0; i < conf->cnt; i++) + { + if (message_add_payload (msg, ISAKMP_PAYLOAD_TRANSFORM, transform[i], + transform_len[i], update_nextp)) + goto bail_out; + update_nextp = 1; + transform[i] = 0; + } + + /* Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. */ + ie->sa_i_b_len = sa_len + proposal_len + transforms_len - ISAKMP_GEN_SZ; + ie->sa_i_b = malloc (ie->sa_i_b_len); + if (!ie->sa_i_b) + goto bail_out; + memcpy (ie->sa_i_b, + TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA])->p + ISAKMP_GEN_SZ, + sa_len - ISAKMP_GEN_SZ); + memcpy (ie->sa_i_b + sa_len - ISAKMP_GEN_SZ, + TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL])->p, + proposal_len); + transforms_len = 0; + for (i = 0, p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); + i < conf->cnt; i++, p = TAILQ_NEXT (p, link)) + { + memcpy (ie->sa_i_b + sa_len + proposal_len + transforms_len + - ISAKMP_GEN_SZ, + p->p, transform_len[i]); + transforms_len += transform_len[i]; + } + + return 0; + + bail_out: + if (sa_buf) + free (sa_buf); + if (proposal) + free (proposal); + if (transform) + { + for (i = 0; i < conf->cnt; i++) + if (transform[i]) + free (transform[i]); + free (transform); + } + if (transform_len) + free (transform_len); + if (conf) + conf_free_list (conf); + return -1; +} + +/* Figure out what transform the responder chose. */ +static int +initiator_recv_SA (struct message *msg) +{ + struct sa *sa; + struct proto *proto; + struct payload *sa_p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); + struct payload *prop = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL]); + struct payload *xf = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); + + /* + * IKE requires that only one SA with only one proposal exists and since + * we are getting an answer on our transform offer, only one transform. + */ + if (TAILQ_NEXT (sa_p, link) || TAILQ_NEXT (prop, link) + || TAILQ_NEXT (xf, link)) + { + log_print ("initiator_recv_SA: " + "multiple SA, proposal or transform payloads in main mode"); + /* XXX Is there a better notification type? */ + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0); + return -1; + } + + sa = TAILQ_FIRST (&msg->exchange->sa_list); + + /* Build the protection suite in our SA. */ + if (sa_add_transform (sa, xf, msg->exchange->initiator, &proto)) + { + /* XXX Log? */ + return -1; + } + + /* XXX Check that the chosen transform matches an offer. */ + ipsec_decode_transform (msg, sa, TAILQ_FIRST (&sa->protos), xf->p); + + /* Mark the SA as handled. */ + sa_p->flags |= PL_MARK; + return 0; +} + +/* Send our public DH value and a nonce to the responder. */ +static int +initiator_send_KE_NONCE (struct message *msg) +{ + struct ipsec_exch *ie = msg->exchange->data; + + ie->g_x_len = dh_getlen (ie->group); + + /* XXX I want a better way to specify the nonce's size. */ + return send_KE_NONCE (msg, 16); +} + +/* Accept receptor's public DH value and nonce. */ +static int +initiator_recv_KE_NONCE (struct message *msg) +{ + if (recv_KE_NONCE (msg)) + return -1; + + return post_exchange_KE_NONCE (msg); +} + +static int +initiator_send_ID_AUTH (struct message *msg) +{ + msg->exchange->flags |= EXCHANGE_FLAG_ENCRYPT; + return send_ID_AUTH (msg); +} + +static int +initiator_recv_ID_AUTH (struct message *msg) +{ + return recv_ID_AUTH (msg); +} + +/* + * Accept a set of transforms offered by the initiator and chose one we can + * handle. + */ +static int +responder_recv_SA (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct sa *sa = TAILQ_FIRST (&exchange->sa_list); + struct payload *sa_p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); + struct payload *prop = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL]); + struct ipsec_exch *ie = exchange->data; + + /* Mark the SA as handled. */ + sa_p->flags |= PL_MARK; + + /* IKE requires that only one SA with only one proposal exists. */ + if (TAILQ_NEXT (sa_p, link) || TAILQ_NEXT (prop, link)) + { + log_print ("responder_recv_SA: " + "multiple SA or proposal payloads in main mode"); + /* XXX Is there a better notification type? */ + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0); + return -1; + } + + /* Chose a transform from the SA. */ + if (message_negotiate_sa (msg) || !TAILQ_FIRST (&sa->protos)) + return -1; + + /* XXX Move into message_negotiate_sa? */ + ipsec_decode_transform (msg, sa, TAILQ_FIRST (&sa->protos), + TAILQ_FIRST (&sa->protos)->chosen->p); + + /* + * Check that the mandatory attributes: encryption, hash, authentication + * method and Diffie-Hellman group description, has been supplied. + */ + if (!exchange->crypto || !ie->hash || !ie->ike_auth || !ie->group) + { + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0); + return -1; + } + + /* Save the body for later hash computation. */ + ie->sa_i_b_len = GET_ISAKMP_GEN_LENGTH (sa_p->p) - ISAKMP_GEN_SZ; + ie->sa_i_b = malloc (ie->sa_i_b_len); + if (!ie->sa_i_b) + { + /* XXX How to log and notify peer? */ + return -1; + } + memcpy (ie->sa_i_b, sa_p->p + ISAKMP_GEN_SZ, ie->sa_i_b_len); + + return 0; +} + +/* Reply with the transform we chose. */ +static int +responder_send_SA (struct message *msg) +{ + /* Add the SA payload with the transform that was chosen. */ + return message_add_sa_payload (msg); +} + +/* Accept initiator's public DH value and nonce. */ +static int +responder_recv_KE_NONCE (struct message *msg) +{ + return recv_KE_NONCE (msg); +} + +/* Send our public DH value and a nonce to the initiator. */ +static int +responder_send_KE_NONCE (struct message *msg) +{ + /* XXX Should we really just use the initiator's nonce size? */ + if (send_KE_NONCE (msg, msg->exchange->nonce_i_len)) + return -1; + + /* + * Calculate DH values & key material in parallel with the message going + * on a roundtrip over the wire. + */ + message_register_post_send (msg, + (void (*) (struct message *)) + post_exchange_KE_NONCE); + + return 0; +} + +/* Receive ID and HASH and check that the exchange has been consistent. */ +static int +responder_recv_ID_AUTH (struct message *msg) +{ + return recv_ID_AUTH (msg); +} + +static int +responder_send_ID_AUTH (struct message *msg) +{ + return send_ID_AUTH (msg); +} + +/* Send our public DH value and a nonce to the peer. */ +static int +send_KE_NONCE (struct message *msg, size_t nonce_sz) +{ + /* Public DH key. */ + if (ipsec_gen_g_x (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + /* Generate a nonce, and add it to the message. */ + if (exchange_gen_nonce (msg, nonce_sz)) + { + /* XXX Log? */ + return -1; + } + + /* Try to add certificates which are acceptable for the CERTREQs */ + if (exchange_add_certs (msg)) + { + /* XXX Log? */ + return -1; + } + + return 0; +} + +/* Receive our peer's public DH value and nonce. */ +static int +recv_KE_NONCE (struct message *msg) +{ + /* Copy out the initiator's DH public value. */ + if (ipsec_save_g_x (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + /* Copy out the initiator's nonce. */ + if (exchange_save_nonce (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + /* Copy out the initiator's cert requests. */ + if (exchange_save_certreq (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + return 0; +} + +/* + * Compute DH values and key material. This is done in a post-send function + * as that means we can do parallel work in both the initiator and responder + * thus speeding up exchanges. + */ +static int +post_exchange_KE_NONCE (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + struct hash *hash = ie->hash; + enum cryptoerr err; + + /* Compute Diffie-Hellman shared value. */ + ie->g_xy = malloc (ie->g_x_len); + if (!ie->g_xy) + { + /* XXX How to log and notify peer? */ + return -1; + } + dh_create_shared (ie->group, ie->g_xy, + exchange->initiator ? ie->g_xr : ie->g_xi); + log_debug_buf (LOG_MISC, 80, "post_exchange_KE_NONCE: g^xy", ie->g_xy, + ie->g_x_len); + + /* Compute the SKEYID depending on the authentication method. */ + ie->skeyid = ie->ike_auth->gen_skeyid (exchange, &ie->skeyid_len); + if (!ie->skeyid) + { + /* XXX Log and teardown? */ + return -1; + } + log_debug_buf (LOG_MISC, 80, "post_exchange_KE_NONCE: SKEYID", + ie->skeyid, ie->skeyid_len); + + /* SKEYID_d. */ + ie->skeyid_d = malloc (ie->skeyid_len); + if (!ie->skeyid_d) + { + /* XXX Log and teardown? */ + return -1; + } + prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, + ie->skeyid_len); + if (!prf) + { + /* XXX Log and teardown? */ + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); + prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + prf->Update (prf->prfctx, "\0", 1); + prf->Final (ie->skeyid_d, prf->prfctx); + log_debug_buf (LOG_MISC, 80, "post_exchange_KE_NONCE: SKEYID_d", + ie->skeyid_d, ie->skeyid_len); + + /* SKEYID_a. */ + ie->skeyid_a = malloc (ie->skeyid_len); + if (!ie->skeyid_a) + { + /* XXX Log & teardown? */ + prf_free (prf); + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, ie->skeyid_d, ie->skeyid_len); + prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); + prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + prf->Update (prf->prfctx, "\1", 1); + prf->Final (ie->skeyid_a, prf->prfctx); + log_debug_buf (LOG_MISC, 80, "post_exchange_KE_NONCE: SKEYID_a", + ie->skeyid_a, ie->skeyid_len); + + /* SKEYID_e. */ + ie->skeyid_e = malloc (ie->skeyid_len); + if (!ie->skeyid_e) + { + /* XXX Log and teardown? */ + prf_free (prf); + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, ie->skeyid_a, ie->skeyid_len); + prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); + prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + prf->Update (prf->prfctx, "\2", 1); + prf->Final (ie->skeyid_e, prf->prfctx); + prf_free (prf); + log_debug_buf (LOG_MISC, 80, "post_exchange_KE_NONCE: SKEYID_e", + ie->skeyid_e, ie->skeyid_len); + + /* Key length determination. */ + if (!exchange->key_length) + exchange->key_length = exchange->crypto->keymax; + + /* Derive a longer key from skeyid_e */ + if (ie->skeyid_len < exchange->key_length) + { + u_int16_t len, keylen; + u_int8_t *key, *p; + + prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid_e, ie->skeyid_len); + if (!prf) + { + /* XXX - notify peer */ + return -1; + } + + /* Make keylen a multiple of prf->blocksize */ + keylen = exchange->key_length; + if (keylen % prf->blocksize) + keylen += prf->blocksize - (keylen % prf->blocksize); + + if ((key = malloc (keylen)) == NULL) + { + /* XXX - notify peer */ + return -1; + } + + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, "\0", 1); + prf->Final (key, prf->prfctx); + + for (len = prf->blocksize, p = key; len < exchange->key_length; + len += prf->blocksize, p += prf->blocksize) + { + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, p, prf->blocksize); + prf->Final (p + prf->blocksize, prf->prfctx); + } + prf_free (prf); + + /* Set up our keystate using the derived encryption key */ + exchange->keystate + = crypto_init (exchange->crypto, key, exchange->key_length, &err); + + free (key); + } + else + /* Set up our keystate using the raw skeyid_e */ + exchange->keystate = crypto_init (exchange->crypto, ie->skeyid_e, + exchange->key_length, &err); + /* Special handling for DES weak keys */ + if (!exchange->keystate && err == EWEAKKEY + && (exchange->key_length << 1) <= ie->skeyid_len) + { + log_print ("post_exchange_KE_NONCE: weak key, trying subseq. skeyid_e"); + exchange->keystate = crypto_init (exchange->crypto, + ie->skeyid_e + exchange->key_length, + exchange->key_length, &err); + } + + if (!exchange->keystate) + { + log_print ("post_exchange_KE_NONCE: " + "exchange->crypto->init () failed: %d", err); + + /* + * XXX We really need to know if problems are of transient nature + * or fatal (like failed assertions etc.) + */ + return -1; + } + + /* Setup IV. XXX Only for CBC transforms, no? */ + hash->Init (hash->ctx); + hash->Update (hash->ctx, ie->g_xi, ie->g_x_len); + hash->Update (hash->ctx, ie->g_xr, ie->g_x_len); + hash->Final (hash->digest, hash->ctx); + crypto_init_iv (exchange->keystate, hash->digest, + exchange->crypto->blocksize); + + return 0; +} + +static int +send_ID_AUTH (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + u_int8_t *buf; + char header[80]; + size_t sz; + struct sockaddr *src; + int src_len; + int initiator = exchange->initiator; + u_int8_t **id; + size_t *id_len; + + /* Choose the right fields to fill-in. */ + id = initiator ? &exchange->id_i : &exchange->id_r; + id_len = initiator ? &exchange->id_i_len : &exchange->id_r_len; + + /* XXX This begs to be more dynamic. */ + sz = ISAKMP_ID_DATA_OFF + 4; + buf = malloc (sz); + if (!buf) + { + /* XXX Log? */ + return -1; + } + msg->transport->vtbl->get_src (msg->transport, &src, &src_len); + /* XXX Assumes IPv4. */ + SET_ISAKMP_ID_TYPE (buf, IPSEC_ID_IPV4_ADDR); + SET_IPSEC_ID_PROTO (buf + ISAKMP_ID_DOI_DATA_OFF, 0); + SET_IPSEC_ID_PORT (buf + ISAKMP_ID_DOI_DATA_OFF, 0); + /* Already in network byteorder. */ + memcpy (buf + ISAKMP_ID_DATA_OFF, + &((struct sockaddr_in *)src)->sin_addr.s_addr, sizeof (in_addr_t)); + if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, buf, sz, 1)) + { + /* XXX Log? */ + free (buf); + return -1; + } + *id_len = sz - ISAKMP_GEN_SZ; + *id = malloc (*id_len); + if (!*id) + { + /* XXX Log? */ + return -1; + } + memcpy (*id, buf + ISAKMP_GEN_SZ, *id_len); + snprintf (header, 80, "send_ID_AUTH: %s", + constant_name (ipsec_id_cst, GET_ISAKMP_ID_TYPE (buf))); + log_debug_buf (LOG_MISC, 40, header, buf + ISAKMP_ID_DATA_OFF, + sz - ISAKMP_ID_DATA_OFF); + + if (ie->ike_auth->encode_hash (msg) == -1) + { + /* XXX Log? */ + return -1; + } + + /* + * XXX Many people say the COMMIT flag is just junk, especially in Phase 1. + */ +#ifdef notyet + if ((exchange->flags & EXCHANGE_FLAG_COMMITTED) == 0) + exchange->flags |= EXCHANGE_FLAG_I_COMMITTED; +#endif + + return 0; +} + +/* Receive ID and HASH and check that the exchange has been consistent. */ +static int +recv_ID_AUTH (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct payload *payload; + struct prf *prf; + struct hash *hash = ie->hash; + char header[80]; + size_t hashsize = hash->hashsize; + int initiator = exchange->initiator; + u_int8_t **hash_p, **id; + size_t *id_len; + + /* Choose the right fields to fill in */ + hash_p = initiator ? &ie->hash_r : &ie->hash_i; + id = initiator ? &exchange->id_r : &exchange->id_i; + id_len = initiator ? &exchange->id_r_len : &exchange->id_i_len; + + /* XXX Do I really have to save the ID in the SA? */ + payload = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_ID]); + *id_len = GET_ISAKMP_GEN_LENGTH (payload->p) - ISAKMP_GEN_SZ; + *id = malloc (*id_len); + if (!*id) + { + /* XXX Log? */ + return -1; + } + memcpy (*id, payload->p + ISAKMP_GEN_SZ, *id_len); + snprintf (header, 80, "recv_ID_AUTH: %s", + constant_name (ipsec_id_cst, GET_ISAKMP_ID_TYPE (payload->p))); + log_debug_buf (LOG_MISC, 40, header, payload->p + ISAKMP_ID_DATA_OFF, + *id_len + ISAKMP_GEN_SZ - ISAKMP_ID_DATA_OFF); + payload->flags |= PL_MARK; + + /* The decoded hash will be in ie->hash_r or ie->hash_i */ + if (ie->ike_auth->decode_hash (msg) == -1) + { + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0); + return -1; + } + + /* Allocate the prf and start calculating his HASH. */ + prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); + if (!prf) + { + /* XXX Log? */ + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len); + prf->Update (prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len); + prf->Update (prf->prfctx, + exchange->cookies + + (initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF), + ISAKMP_HDR_ICOOKIE_LEN); + prf->Update (prf->prfctx, + exchange->cookies + + (initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF), + ISAKMP_HDR_ICOOKIE_LEN); + prf->Update (prf->prfctx, ie->sa_i_b, ie->sa_i_b_len); + prf->Update (prf->prfctx, *id, *id_len); + prf->Final (hash->digest, prf->prfctx); + prf_free (prf); + snprintf (header, 80, "recv_ID_AUTH: computed HASH_%c", + initiator ? 'R' : 'I'); + log_debug_buf (LOG_MISC, 80, header, hash->digest, hashsize); + + /* Check that the hash we got matches the one we computed. */ + if (memcmp (*hash_p, hash->digest, hashsize) != 0) + { + /* XXX Log? */ + return -1; + } + + return 0; +} diff --git a/sbin/isakmpd/ike_main_mode.h b/sbin/isakmpd/ike_main_mode.h new file mode 100644 index 00000000000..68d38b26074 --- /dev/null +++ b/sbin/isakmpd/ike_main_mode.h @@ -0,0 +1,44 @@ +/* $Id: ike_main_mode.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _IKE_MAIN_MODE_H_ +#define _IKE_MAIN_MODE_H_ + +struct message; + +extern int (*ike_main_mode_initiator[]) (struct message *msg); +extern int (*ike_main_mode_responder[]) (struct message *msg); + +#endif /* _IKE_MAIN_MODE_H_ */ diff --git a/sbin/isakmpd/ike_quick_mode.c b/sbin/isakmpd/ike_quick_mode.c new file mode 100644 index 00000000000..0f566c6bbe0 --- /dev/null +++ b/sbin/isakmpd/ike_quick_mode.c @@ -0,0 +1,1069 @@ +/* $Id: ike_quick_mode.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <stdlib.h> +#include <string.h> + +#include "attribute.h" +#include "conf.h" +#include "dh.h" +#include "doi.h" +#include "exchange.h" +#include "hash.h" +#include "ike_quick_mode.h" +#include "ipsec.h" +#include "log.h" +#include "math_group.h" +#include "message.h" +#include "prf.h" +#include "sa.h" +#include "transport.h" + +static void gen_g_xy (struct message *); +static int initiator_send_HASH_SA_NONCE (struct message *); +static int initiator_recv_HASH_SA_NONCE (struct message *); +static int initiator_send_HASH (struct message *); +static void post_quick_mode (struct message *); +static int responder_recv_HASH_SA_NONCE (struct message *); +static int responder_send_HASH_SA_NONCE (struct message *); +static int responder_recv_HASH (struct message *); + +int (*ike_quick_mode_initiator[]) (struct message *) = { + initiator_send_HASH_SA_NONCE, + initiator_recv_HASH_SA_NONCE, + initiator_send_HASH +}; + +int (*ike_quick_mode_responder[]) (struct message *) = { + responder_recv_HASH_SA_NONCE, + responder_send_HASH_SA_NONCE, + responder_recv_HASH +}; + +/* + * Offer several sets of transforms to the responder. + * XXX Split this huge function up and look for common code with main mode. + */ +static int +initiator_send_HASH_SA_NONCE (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct doi *doi = exchange->doi; + struct ipsec_exch *ie = exchange->data; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + u_int8_t ***transform = 0, ***new_transform; + u_int8_t **proposal = 0, **new_proposal; + u_int8_t *sa_buf = 0, *attr, *saved_nextp_sa, *saved_nextp_prop; + u_int8_t *buf; + u_int8_t *spi; + size_t spi_sz; + struct prf *prf; + struct hash *hash = hash_get (isa->hash); + size_t hashsize = hash->hashsize; + size_t proposal_len = 0, proposals_len = 0, sa_len; + size_t **transform_len = 0, **new_transform_len; + size_t *transforms_len = 0, *new_transforms_len; + int *transform_cnt = 0, *new_transform_cnt; + char header[80]; + int i, suite_no, prop_no, prot_no, xf_no, value, update_nextp, protocol_num; + int prop_cnt = 0; + struct proto *proto; + struct conf_list *suite_conf = 0, *prot_conf = 0, *xf_conf = 0, *life_conf; + struct conf_list_node *suite, *prot, *xf, *life; + struct constant_map *id_map; + char *protocol_id, *transform_id; + + /* We want a HASH payload to start with. XXX Share with ike_main_mode.c? */ + buf = malloc (ISAKMP_HASH_SZ + hashsize); + if (!buf) + { + /* XXX Log? */ + return -1; + } + if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, buf, + ISAKMP_HASH_SZ + hashsize, 1)) + { + /* XXX Log? */ + free (buf); + return -1; + } + + /* Get the list of protocol suites. */ + suite_conf = conf_get_list ("Quick mode initiator", "Offered-suites"); + if (!suite_conf) + return -1; + + for (suite = TAILQ_FIRST (&suite_conf->fields), suite_no = prop_no = 0; + suite_no < suite_conf->cnt; + suite_no++, suite = TAILQ_NEXT (suite, link)) + { + /* Now get each protocol in this specific protocol suite. */ + prot_conf = conf_get_list (suite->field, "Protocols"); + if (!prot_conf) + goto bail_out; + + for (prot = TAILQ_FIRST (&prot_conf->fields), prot_no = 0; + prot_no < prot_conf->cnt; + prot_no++, prot = TAILQ_NEXT (prot, link)) + { + /* Make sure we have a proposal/transform vectors. */ + if (prop_no >= prop_cnt) + { + /* This resize algorithm is completely arbitrary. */ + prop_cnt = 2 * prop_cnt + 10; + new_proposal = realloc (proposal, prop_cnt * sizeof *proposal); + if (!new_proposal) + goto bail_out; + proposal = new_proposal; + + new_transforms_len = realloc (transforms_len, + prop_cnt * sizeof *transforms_len); + if (!new_transforms_len) + goto bail_out; + transforms_len = new_transforms_len; + + new_transform = realloc (transform, + prop_cnt * sizeof *transform); + if (!new_transform) + goto bail_out; + transform = new_transform; + + new_transform_cnt = realloc (transform_cnt, + prop_cnt * sizeof *transform_cnt); + if (!new_transform_cnt) + goto bail_out; + transform_cnt = new_transform_cnt; + + new_transform_len = realloc (transform_len, + prop_cnt * sizeof *transform_len); + if (!new_transform_len) + goto bail_out; + transform_len = new_transform_len; + } + + protocol_id = conf_get_str (prot->field, "PROTOCOL_ID"); + if (!protocol_id) + goto bail_out; + + /* XXX Not too beautiful, but do we have a choice? */ + id_map = strcasecmp (protocol_id, "IPSEC_AH") == 0 ? ipsec_ah_cst + : strcasecmp (protocol_id, "IPSEC_ESP") == 0 ? ipsec_esp_cst + : strcasecmp (protocol_id, "IPCOMP") == 0 ? ipsec_ipcomp_cst : 0; + if (!id_map) + goto bail_out; + + /* Now get each transform we offer for this protocol. */ + xf_conf = conf_get_list (prot->field, "Transforms"); + if (!xf_conf) + goto bail_out; + transform_cnt[prop_no] = xf_conf->cnt; + + transform[prop_no] = calloc (transform_cnt[prop_no], + sizeof **transform); + if (!transform[prop_no]) + goto bail_out; + transform_len[prop_no] + = calloc (transform_cnt[prop_no], sizeof **transform_len); + if (!transform_len[prop_no]) + goto bail_out; + + transforms_len[prop_no] = 0; + for (xf = TAILQ_FIRST (&xf_conf->fields), xf_no = 0; + xf_no < transform_cnt[prop_no]; + xf_no++, xf = TAILQ_NEXT (xf, link)) + { + + /* XXX The sizing needs to be dynamic. */ + transform[prop_no][xf_no] = calloc (ISAKMP_TRANSFORM_SA_ATTRS_OFF + + 9 * ISAKMP_ATTR_VALUE_OFF, + 1); + if (!transform[prop_no][xf_no]) + goto bail_out; + SET_ISAKMP_TRANSFORM_NO (transform[prop_no][xf_no], xf_no + 1); + + transform_id = conf_get_str (xf->field, "TRANSFORM_ID"); + if (!transform_id) + goto bail_out; + SET_ISAKMP_TRANSFORM_ID (transform[prop_no][xf_no], + constant_value (id_map, transform_id)); + SET_ISAKMP_TRANSFORM_RESERVED (transform[prop_no][xf_no], 0); + + attr = transform[prop_no][xf_no] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; + + /* + * Life durations are special, we should be able to specify + * several, one per type. + */ + life_conf = conf_get_list (xf->field, "Life"); + if (life_conf) + { + for (life = TAILQ_FIRST (&life_conf->fields); life; + life = TAILQ_NEXT (life, link)) + { + attribute_set_constant (life->field, "SA_LIFE_TYPE", + ipsec_duration_cst, + IPSEC_ATTR_SA_LIFE_TYPE, &attr); + + /* XXX Does only handle 16-bit entities! */ + value = conf_get_num (life->field, "SA_LIFE_DURATION"); + if (value) + attr + = attribute_set_basic (attr, + IPSEC_ATTR_SA_LIFE_DURATION, + value); + } + } + + attribute_set_constant (xf->field, "ENCAPSULATION_MODE", + ipsec_encap_cst, + IPSEC_ATTR_ENCAPSULATION_MODE, &attr); + + attribute_set_constant (xf->field, "AUTHENTICATION_ALGORITHM", + ipsec_auth_cst, + IPSEC_ATTR_AUTHENTICATION_ALGORITHM, + &attr); + + attribute_set_constant (xf->field, "GROUP_DESCRIPTION", + ike_group_desc_cst, + IKE_ATTR_GROUP_DESCRIPTION, &attr); + + value = conf_get_num (xf->field, "KEY_LENGTH"); + if (value) + attr = attribute_set_basic (attr, IPSEC_ATTR_KEY_LENGTH, + value); + + value = conf_get_num (xf->field, "KEY_ROUNDS"); + if (value) + attr = attribute_set_basic (attr, IPSEC_ATTR_KEY_ROUNDS, + value); + + value = conf_get_num (xf->field, "COMPRESS_DICTIONARY_SIZE"); + if (value) + attr + = attribute_set_basic (attr, + IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE, + value); + + value = conf_get_num (xf->field, "COMPRESS_PRIVATE_ALGORITHM"); + if (value) + attr + = attribute_set_basic (attr, + IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM, + value); + + /* Record the real transform size. */ + transforms_len[prop_no] += (transform_len[prop_no][xf_no] + = attr - transform[prop_no][xf_no]); + } + + /* + * Get SPI from application. + * XXX Should we care about unknown constants? + */ + protocol_num = constant_value (ipsec_proto_cst, protocol_id); + spi = doi->get_spi (&spi_sz, protocol_num, msg); + if (spi_sz && !spi) + goto bail_out; + + proposal_len = ISAKMP_PROP_SPI_OFF + spi_sz; + proposals_len += proposal_len + transforms_len[prop_no]; + proposal[prop_no] = malloc (proposal_len); + if (!proposal[prop_no]) + goto bail_out; + SET_ISAKMP_PROP_NO (proposal[prop_no], suite_no + 1); + SET_ISAKMP_PROP_PROTO (proposal[prop_no], protocol_num); + + /* XXX I would like to see this factored out. */ + proto = calloc (1, sizeof *proto); + if (!proto) + goto bail_out; + if (doi->proto_size) + { + proto->data = calloc (1, doi->proto_size); + if (!proto->data) + goto bail_out; + } + proto->no = suite_no + 1; + proto->proto = protocol_num; + proto->sa = TAILQ_FIRST (&exchange->sa_list); + TAILQ_INSERT_TAIL (&TAILQ_FIRST (&exchange->sa_list)->protos, proto, + link); + + SET_ISAKMP_PROP_SPI_SZ (proposal[prop_no], spi_sz); + memcpy (proposal[prop_no] + ISAKMP_PROP_SPI_OFF, spi, spi_sz); + proto->spi_sz[exchange->initiator] = spi_sz; + proto->spi[exchange->initiator] = spi; + + SET_ISAKMP_PROP_NTRANSFORMS (proposal[prop_no], + transform_cnt[prop_no]); + prop_no++; + } + } + + sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; + sa_buf = malloc (sa_len); + if (!sa_buf) + goto bail_out; + SET_ISAKMP_SA_DOI (sa_buf, IPSEC_DOI_IPSEC); + SET_IPSEC_SIT_SIT (sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); + + /* + * Add the payloads. As this is a SA, we need to recompute the + * lengths of the payloads containing others. We also need to + * reset these payload's "next payload type" field. + */ + if (message_add_payload (msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) + goto bail_out; + SET_ISAKMP_GEN_LENGTH (sa_buf, sa_len + proposals_len); + sa_buf = 0; + + update_nextp = 0; + saved_nextp_sa = msg->nextp; + for (i = 0; i < prop_no; i++) + { + if (message_add_payload (msg, ISAKMP_PAYLOAD_PROPOSAL, proposal[i], + proposal_len, update_nextp)) + goto bail_out; + SET_ISAKMP_GEN_LENGTH (proposal[i], proposal_len + transforms_len[i]); + proposal[i] = 0; + + update_nextp = 0; + saved_nextp_prop = msg->nextp; + for (xf_no = 0; xf_no < transform_cnt[i]; xf_no++) + { + if (message_add_payload (msg, ISAKMP_PAYLOAD_TRANSFORM, + transform[i][xf_no], + transform_len[i][xf_no], update_nextp)) + goto bail_out; + update_nextp = 1; + transform[i][xf_no] = 0; + } + msg->nextp = saved_nextp_prop; + update_nextp = 1; + } + msg->nextp = saved_nextp_sa; + + /* + * Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. + */ + ie->sa_i_b = message_copy (msg, ISAKMP_GEN_SZ, &ie->sa_i_b_len); + if (!ie->sa_i_b) + goto bail_out; + + /* + * Generate a nonce, and add it to the message. + * XXX I want a better way to specify the nonce's size. + */ + if (exchange_gen_nonce (msg, 16)) + { + /* XXX Log? */ + return -1; + } + + /* Generate optional KEY_EXCH payload. */ + if (ie->group) + { + ie->g_x_len = dh_getlen (ie->group); + + if (ipsec_gen_g_x (msg)) + { + /* XXX Log? */ + return -1; + } + } + + /* XXX Generate optional client ID payloads. */ + + /* Allocate the prf and start calculating our HASH(1). XXX Share? */ + log_debug_buf (LOG_MISC, 90, "initiator_send_HASH_SA_NONCE: SKEYID_a", + isa->skeyid_a, isa->skeyid_len); + prf = prf_alloc (isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); + if (!prf) + { + /* XXX Log? */ + return -1; + } + prf->Init (prf->prfctx); + log_debug_buf (LOG_MISC, 90, "initiator_send_HASH_SA_NONCE: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + + /* Loop over all payloads after HASH(1). */ + for (i = 2; i < msg->iovlen; i++) + { + /* XXX Misleading payload type printouts. */ + snprintf (header, 80, + "initiator_send_HASH_SA_NONCE: payload %d after HASH(1)", + i - 1); + log_debug_buf (LOG_MISC, 90, header, msg->iov[i].iov_base, + msg->iov[i].iov_len); + prf->Update (prf->prfctx, msg->iov[i].iov_base, msg->iov[i].iov_len); + } + prf->Final (buf + ISAKMP_HASH_DATA_OFF, prf->prfctx); + prf_free (prf); + log_debug_buf (LOG_MISC, 80, "initiator_send_HASH_SA_NONCE: HASH(1)", + buf + ISAKMP_HASH_DATA_OFF, hashsize); + + return 0; + + bail_out: + if (sa_buf) + free (sa_buf); + if (proposal) + { + for (i = 0; i < prop_no; i++) + { + if (proposal[i]) + free (proposal[i]); + if (transform[i]) + { + for (xf_no = 0; xf_no < xf_conf->cnt; xf_no++) + if (transform[i][xf_no]) + free (transform[i][xf_no]); + free (transform[i]); + } + if (transform_len[i]) + free (transform_len[i]); + } + free (proposal); + free (transforms_len); + free (transform); + free (transform_len); + free (transform_cnt); + } + if (xf_conf) + conf_free_list (xf_conf); + if (prot_conf) + conf_free_list (prot_conf); + if (suite_conf) + conf_free_list (suite_conf); + return -1; +} + +/* Figure out what transform the responder chose. */ +static int +initiator_recv_HASH_SA_NONCE (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *sa; + struct proto *proto; + struct payload *sa_p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); + struct payload *xf; + struct payload *hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + struct payload *kep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]); + struct prf *prf; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct hash *hash = hash_get (isa->hash); + size_t hashsize = hash->hashsize; + u_int8_t *rest; + size_t rest_len; + + /* + * As we are getting an answer on our transform offer, only one transform + * should be given. + * + * XXX Currently we only support negotiating one SA per quick mode run. + */ + if (TAILQ_NEXT (sa_p, link)) + { + log_print ("initiator_recv_HASH_SA_NONCE: " + "multiple SA payloads in quick mode"); + /* XXX Is there a better notification type? */ + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0); + return -1; + } + + sa = TAILQ_FIRST (&msg->exchange->sa_list); + + /* Build the protection suite in our SA. */ + for (xf = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); xf; + xf = TAILQ_NEXT (xf, link)) + { + /* + * XXX We could check that the proposal each transform belongs to + * is unique. + */ + + if (sa_add_transform (sa, xf, exchange->initiator, &proto)) + { + /* XXX Log? */ + return -1; + } + + /* XXX Check that the chosen transform matches an offer. */ + + ipsec_decode_transform (msg, sa, proto, xf->p); + } + + /* Now remove offers that we don't need anymore. */ + for (proto = TAILQ_FIRST (&sa->protos); proto; + proto = TAILQ_NEXT (proto, link)) + if (!proto->chosen) + proto_free (proto); + + /* Mark the SA as handled. */ + sa_p->flags |= PL_MARK; + + /* Allocate the prf and start calculating our HASH(1). XXX Share? */ + log_debug_buf (LOG_MISC, 90, "initiator_recv_HASH_SA_NONCE: SKEYID_a", + isa->skeyid_a, isa->skeyid_len); + prf = prf_alloc (isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); + if (!prf) + { + /* XXX Log? */ + return -1; + } + prf->Init (prf->prfctx); + log_debug_buf (LOG_MISC, 90, "initiator_recv_HASH_SA_NONCE: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + log_debug_buf (LOG_MISC, 90, "initiator_recv_HASH_SA_NONCE: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len); + prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + rest = hashp->p + GET_ISAKMP_GEN_LENGTH (hashp->p); + rest_len = (GET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base) + - (rest - (u_int8_t*)msg->iov[0].iov_base)); + log_debug_buf (LOG_MISC, 90, + "initiator_recv_HASH_SA_NONCE: payloads after HASH(2)", rest, + rest_len); + prf->Update (prf->prfctx, rest, rest_len); + prf->Final (hash->digest, prf->prfctx); + prf_free (prf); + log_debug_buf (LOG_MISC, 80, + "initiator_recv_HASH_SA_NONCE: computed HASH(2)", + hash->digest, hashsize); + if (memcmp (hashp->p + ISAKMP_HASH_DATA_OFF, hash->digest, hashsize) != 0) + { + /* XXX Log & notify? */ + return -1; + } + /* Mark the HASH as handled. */ + hashp->flags |= PL_MARK; + + /* XXX Errors possible? */ + ie->group = group_get (isa->group_desc); + + /* Copy out the initiator's nonce. */ + if (exchange_save_nonce (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + /* Handle the optional KEY_EXCH payload. */ + if (kep && ipsec_save_g_x (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + /* XXX Handle optional client ID payloads. */ + + return 0; +} + +static int +initiator_send_HASH (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct prf *prf; + u_int8_t *buf; + struct hash *hash = hash_get (isa->hash); + size_t hashsize = hash->hashsize; + + /* We want a HASH payload to start with. XXX Share with ike_main_mode.c? */ + buf = malloc (ISAKMP_HASH_SZ + hashsize); + if (!buf) + { + /* XXX Log? */ + return -1; + } + if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, buf, + ISAKMP_HASH_SZ + hashsize, 1)) + { + /* XXX Log? */ + free (buf); + return -1; + } + + /* Allocate the prf and start calculating our HASH(3). XXX Share? */ + log_debug_buf (LOG_MISC, 90, "initiator_send_HASH: SKEYID_a", isa->skeyid_a, + isa->skeyid_len); + prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); + if (!prf) + { + /* XXX Log? */ + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, "\0", 1); + log_debug_buf (LOG_MISC, 90, "initiator_send_HASH: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + log_debug_buf (LOG_MISC, 90, "initiator_send_HASH: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len); + prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + log_debug_buf (LOG_MISC, 90, "initiator_send_HASH: NONCE_R_b", + exchange->nonce_r, exchange->nonce_r_len); + prf->Update (prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); + prf->Final (buf + ISAKMP_GEN_SZ, prf->prfctx); + prf_free (prf); + log_debug_buf (LOG_MISC, 90, "initiator_send_HASH: HASH(3)", + buf + ISAKMP_GEN_SZ, hashsize); + + if (ie->group) + message_register_post_send (msg, gen_g_xy); + message_register_post_send (msg, post_quick_mode); + + return 0; +} + +static void +post_quick_mode (struct message *msg) +{ + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + struct sa *sa; + struct proto *proto; + struct ipsec_proto *iproto = proto->data; + u_int8_t *keymat; + int i; + + /* + * Loop over all SA negotiations and do both an in- and an outgoing SA + * per protocol. + */ + for (sa = TAILQ_FIRST (&exchange->sa_list); sa; sa = TAILQ_NEXT (sa, next)) + { + for (proto = TAILQ_FIRST (&sa->protos); proto; + proto = TAILQ_NEXT (proto, link)) + { + iproto = proto->data; + + /* + * There are two SAs for each SA negotiation, incoming and outcoing. + */ + for (i = 0; i < 2; i++) + { + prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_d, + isa->skeyid_len); + if (!prf) + { + /* XXX What to do? */ + continue; + } + + ie->keymat_len = ipsec_keymat_length (proto); + + /* + * We need to roundup the length of the key material buffer + * to a multiple of the PRF´s blocksize as it is generated + * in chunks of that blocksize. + */ + iproto->keymat[i] + = malloc (((ie->keymat_len + prf->blocksize - 1) + / prf->blocksize) * prf->blocksize); + if (!iproto->keymat[i]) + { + /* XXX What to do? */ + free (prf); + continue; + } + + for (keymat = iproto->keymat[i]; + keymat < iproto->keymat[i] + ie->keymat_len; + keymat += prf->blocksize) + { + prf->Init (prf->prfctx); + + if (keymat != iproto->keymat[i]) + { + /* Hash in last round's KEYMAT. */ + log_debug_buf (LOG_MISC, 90, + "post_quick_mode: last KEYMAT", + keymat - prf->blocksize, prf->blocksize); + prf->Update (prf->prfctx, keymat - prf->blocksize, + prf->blocksize); + } + + /* If PFS is used hash in g^xy. */ + if (ie->g_xy) + { + log_debug_buf (LOG_MISC, 90, "post_quick_mode: g^xy", + ie->g_xy, ie->g_x_len); + prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); + } + log_debug (LOG_MISC, 90, + "post_quick_mode: suite %d proto %d", proto->no, + proto->proto); + prf->Update (prf->prfctx, &proto->proto, 1); + log_debug_buf (LOG_MISC, 90, "post_quick_mode: SPI", + proto->spi[i], proto->spi_sz[i]); + prf->Update (prf->prfctx, proto->spi[i], proto->spi_sz[i]); + log_debug_buf (LOG_MISC, 90, "post_quick_mode: Ni_b", + exchange->nonce_i, exchange->nonce_i_len); + prf->Update (prf->prfctx, exchange->nonce_i, + exchange->nonce_i_len); + log_debug_buf (LOG_MISC, 90, "post_quick_mode: Nr_b", + exchange->nonce_r, exchange->nonce_r_len); + prf->Update (prf->prfctx, exchange->nonce_r, + exchange->nonce_r_len); + prf->Final (keymat, prf->prfctx); + } + prf_free (prf); + log_debug_buf (LOG_MISC, 90, "post_quick_mode: KEYMAT", + iproto->keymat[i], ie->keymat_len); + } + } + } +} + +/* + * Accept a set of transforms offered by the initiator and chose one we can + * handle. + * XXX Describe in more detail. + */ +static int +responder_recv_HASH_SA_NONCE (struct message *msg) +{ + struct payload *hashp, *kep; + struct sa *sa; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + u_int8_t *hash, *my_hash; + size_t hash_len; + u_int8_t *pkt = msg->iov[0].iov_base; + u_int8_t group_desc = 0; + int retval = -1; + struct proto *proto; + + hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + hash = hashp->p; + hashp->flags |= PL_MARK; + + /* The HASH payload should be the first one. */ + if (hash != pkt + ISAKMP_HDR_SZ) + { + /* XXX Is there a better notification type? */ + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0); + return -1; + } + hash_len = GET_ISAKMP_GEN_LENGTH (hash); + my_hash = malloc (hash_len - ISAKMP_GEN_SZ); + if (!my_hash) + { + /* XXX Log? */ + return -1; + } + + /* Check the payload's integrity. */ + log_debug_buf (LOG_MISC, 90, "responder_recv_HASH_SA_NONCE: SKEYID_a", + isa->skeyid_a, isa->skeyid_len); + prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); + if (!prf) + { + /* XXX Log? */ + return -1; + } + prf->Init (prf->prfctx); + log_debug_buf (LOG_MISC, 90, "responder_recv_HASH_SA_NONCE: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + log_debug_buf (LOG_MISC, 90, + "responder_recv_HASH_SA_NONCE: message after HASH", + hash + hash_len, + msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); + prf->Update (prf->prfctx, hash + hash_len, + msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); + prf->Final (my_hash, prf->prfctx); + prf_free (prf); + log_debug_buf (LOG_MISC, 90, + "responder_recv_HASH_SA_NONCE: computed HASH(1)", my_hash, + hash_len - ISAKMP_GEN_SZ); + if (memcmp (hash + ISAKMP_GEN_SZ, my_hash, hash_len - ISAKMP_GEN_SZ) != 0) + { + /* XXX Is there a better notification type? */ + message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 0, 0); + return -1; + } + + if (message_negotiate_sa (msg)) + return -1; + + kep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]); + + for (sa = TAILQ_FIRST (&exchange->sa_list); sa; + sa = TAILQ_NEXT (sa, next)) + { + for (proto = TAILQ_FIRST (&sa->protos); proto; + proto = TAILQ_NEXT (proto, link)) + /* XXX we need to have some attributes per proto, not all per SA. */ + ipsec_decode_transform (msg, sa, proto, proto->chosen->p); + + + isa = sa->data; + + /* Check the SA for reasonableness. */ + + /* The group description is mandatory if we got a KEY_EXCH payload. */ + if (kep) + { + if (!isa->group_desc) + { + message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 0, 0); + continue; + } + + /* Also, all SAs must have equal groups. */ + if (!group_desc) + group_desc = isa->group_desc; + else if (group_desc != isa->group_desc) + { + message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 0, 0); + continue; + } + } + + /* At least one SA was accepted. */ + retval = 0; + } + + /* XXX Errors possible? */ + ie->group = group_get (group_desc); + + /* Copy out the initiator's nonce. */ + if (exchange_save_nonce (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + /* Handle the optional KEY_EXCH payload. */ + if (kep && ipsec_save_g_x (msg)) + { + /* XXX How to log and notify peer? */ + return -1; + } + + /* XXX Handle optional client ID payloads. */ + + return retval; +} + +/* Reply with the transform we chose. */ +static int +responder_send_HASH_SA_NONCE (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct prf *prf; + struct hash *hash = hash_get (isa->hash); + size_t hashsize = hash->hashsize; + size_t nonce_sz = exchange->nonce_i_len; + u_int8_t *buf; + int initiator = exchange->initiator; + char header[80]; + int i; + + /* We want a HASH payload to start with. XXX Share with ike_main_mode.c? */ + buf = malloc (ISAKMP_HASH_SZ + hashsize); + if (!buf) + { + /* XXX Log? */ + return -1; + } + if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, buf, + ISAKMP_HASH_SZ + hashsize, 1)) + { + /* XXX Log? */ + free (buf); + return -1; + } + + /* Add the SA payload(s) with the transform(s) that was/were chosen. */ + if (message_add_sa_payload (msg)) + { + /* XXX Log? */ + return -1; + } + + /* Generate a nonce, and add it to the message. */ + if (exchange_gen_nonce (msg, nonce_sz)) + { + /* XXX Log? */ + return -1; + } + + /* Generate optional KEY_EXCH payload. */ + if (ie->group && ipsec_gen_g_x (msg)) + { + /* XXX Log? */ + return -1; + } + + /* XXX Generate optional client ID payload. */ + + /* Allocate the prf and start calculating our HASH(2). XXX Share? */ + log_debug_buf (LOG_MISC, 90, "responder_send_HASH_SA_NONCE: SKEYID_a", + isa->skeyid_a, isa->skeyid_len); + prf = prf_alloc (isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); + if (!prf) + { + /* XXX Log? */ + return -1; + } + prf->Init (prf->prfctx); + log_debug_buf (LOG_MISC, 90, "responder_send_HASH_SA_NONCE: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + log_debug_buf (LOG_MISC, 90, "responder_send_HASH_SA_NONCE: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len); + prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + + /* Loop over all payloads after HASH(2). */ + for (i = 2; i < msg->iovlen; i++) + { + /* XXX Misleading payload type printouts. */ + snprintf (header, 80, + "responder_send_HASH_SA_NONCE: payload %d after HASH(2)", + i - 1); + log_debug_buf (LOG_MISC, 90, header, msg->iov[i].iov_base, + msg->iov[i].iov_len); + prf->Update (prf->prfctx, msg->iov[i].iov_base, msg->iov[i].iov_len); + } + prf->Final (buf + ISAKMP_HASH_DATA_OFF, prf->prfctx); + prf_free (prf); + snprintf (header, 80, "responder_send_HASH_SA_NONCE: HASH_%c", + initiator ? 'I' : 'R'); + log_debug_buf (LOG_MISC, 80, header, buf + ISAKMP_HASH_DATA_OFF, hashsize); + + if (ie->group) + message_register_post_send (msg, gen_g_xy); + + return 0; +} + +static void +gen_g_xy (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + + /* Compute Diffie-Hellman shared value. */ + ie->g_xy = malloc (ie->g_x_len); + if (!ie->g_xy) + { + /* XXX How to log and notify peer? */ + return; + } + dh_create_shared (ie->group, ie->g_xy, + exchange->initiator ? ie->g_xr : ie->g_xi); + log_debug_buf (LOG_MISC, 80, "gen_g_xy: g^xy", ie->g_xy, ie->g_x_len); +} + +static int +responder_recv_HASH (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct prf *prf; + u_int8_t *hash, *my_hash; + size_t hash_len; + struct payload *hashp; + + /* Find HASH(3) and create our own hash, just as big. */ + hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + hash = hashp->p; + hashp->flags |= PL_MARK; + hash_len = GET_ISAKMP_GEN_LENGTH (hash); + my_hash = malloc (hash_len - ISAKMP_GEN_SZ); + if (!my_hash) + { + /* XXX Log? */ + return -1; + } + + /* Allocate the prf and start calculating our HASH(3). XXX Share? */ + log_debug_buf (LOG_MISC, 90, "responder_recv_HASH: SKEYID_a", isa->skeyid_a, + isa->skeyid_len); + prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); + if (!prf) + { + /* XXX Log? */ + free (my_hash); + return -1; + } + prf->Init (prf->prfctx); + prf->Update (prf->prfctx, "\0", 1); + log_debug_buf (LOG_MISC, 90, "responder_recv_HASH: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + log_debug_buf (LOG_MISC, 90, "responder_recv_HASH: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len); + prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + log_debug_buf (LOG_MISC, 90, "responder_recv_HASH: NONCE_R_b", + exchange->nonce_r, exchange->nonce_r_len); + prf->Update (prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); + prf->Final (my_hash, prf->prfctx); + prf_free (prf); + log_debug_buf (LOG_MISC, 90, + "responder_recv_HASH: computed HASH(3)", my_hash, + hash_len - ISAKMP_GEN_SZ); + if (memcmp (hash + ISAKMP_GEN_SZ, my_hash, hash_len - ISAKMP_GEN_SZ) != 0) + { + /* XXX Is there a better notification type? */ + message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 0, 0); + return -1; + } + + post_quick_mode (msg); + + return 0; +} diff --git a/sbin/isakmpd/ike_quick_mode.h b/sbin/isakmpd/ike_quick_mode.h new file mode 100644 index 00000000000..87a4fa699b0 --- /dev/null +++ b/sbin/isakmpd/ike_quick_mode.h @@ -0,0 +1,44 @@ +/* $Id: ike_quick_mode.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _IKE_QUICK_MODE_H_ +#define _IKE_QUICK_MODE_H_ + +struct message; + +extern int (*ike_quick_mode_initiator[]) (struct message *msg); +extern int (*ike_quick_mode_responder[]) (struct message *msg); + +#endif /* _IKE_QUICK_MODE_H_ */ diff --git a/sbin/isakmpd/init.c b/sbin/isakmpd/init.c new file mode 100644 index 00000000000..8e1e3c1eeb7 --- /dev/null +++ b/sbin/isakmpd/init.c @@ -0,0 +1,69 @@ +/* $Id: init.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* XXX This file could easily be built dynamically instead. */ + +#include "app.h" +#include "conf.h" +#include "cookie.h" +#include "doi.h" +#include "exchange.h" +#include "init.h" +#include "ipsec.h" +#include "isakmp_doi.h" +#include "math_group.h" +#include "sa.h" +#include "timer.h" +#include "transport.h" +#include "udp.h" +#include "ui.h" + +void +init () +{ + app_init (); + conf_init (); + doi_init (); + exchange_init (); + group_init (); + ipsec_init (); + isakmp_doi_init (); + timer_init (); + cookie_init (); /* Depends on properly setup timer queue */ + sa_init (); + transport_init (); + udp_init (); + ui_init (); +} diff --git a/sbin/isakmpd/init.h b/sbin/isakmpd/init.h new file mode 100644 index 00000000000..1746607d93b --- /dev/null +++ b/sbin/isakmpd/init.h @@ -0,0 +1,41 @@ +/* $Id: init.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _INIT_H_ +#define _INIT_H_ + +extern void init (void); + +#endif /* _INIT_H_ */ diff --git a/sbin/isakmpd/ipsec.c b/sbin/isakmpd/ipsec.c new file mode 100644 index 00000000000..f8fe5553b86 --- /dev/null +++ b/sbin/isakmpd/ipsec.c @@ -0,0 +1,1067 @@ +/* $Id: ipsec.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include "attribute.h" +#include "constants.h" +#include "crypto.h" +#include "dh.h" +#include "doi.h" +#include "exchange.h" +#include "hash.h" +#include "ike_auth.h" +#include "ike_main_mode.h" +#include "ike_quick_mode.h" +#include "ipsec.h" +#include "ipsec_doi.h" +#include "isakmp.h" +#include "log.h" +#include "math_group.h" +#include "message.h" +#include "prf.h" +#include "sa.h" +#include "sysdep.h" +#include "timer.h" +#include "transport.h" +#include "util.h" + +struct ipsec_decode_arg { + struct message *msg; + struct sa *sa; + struct proto *proto; +}; + +static int ipsec_debug_attribute (u_int16_t, u_int8_t *, u_int16_t, void *); +static void ipsec_delete_spi (struct sa *, struct proto *, int); +static u_int16_t *ipsec_exchange_script (u_int8_t); +static void ipsec_finalize_exchange (struct message *); +static void ipsec_free_exchange_data (void *); +static void ipsec_free_proto_data (void *); +static void ipsec_free_sa_data (void *); +static struct keystate *ipsec_get_keystate (struct message *); +static u_int8_t *ipsec_get_spi (size_t *, u_int8_t, struct message *); +static int ipsec_initiator (struct message *); +static int ipsec_responder (struct message *); +static void ipsec_setup_situation (u_int8_t *); +static size_t ipsec_situation_size (void); +static u_int8_t ipsec_spi_size (u_int8_t); +static int ipsec_validate_attribute (u_int16_t, u_int8_t *, u_int16_t, void *); +static int ipsec_validate_exchange (u_int8_t); +static int ipsec_validate_id_information (u_int8_t, u_int8_t *, u_int8_t *, + size_t, struct exchange *); +static int ipsec_validate_key_information (u_int8_t *, size_t); +static int ipsec_validate_notification (u_int16_t); +static int ipsec_validate_proto (u_int8_t); +static int ipsec_validate_situation (u_int8_t *, size_t *); +static int ipsec_validate_transform_id (u_int8_t, u_int8_t); + +static struct doi ipsec_doi = { + { 0 }, IPSEC_DOI_IPSEC, + sizeof (struct ipsec_exch), sizeof (struct ipsec_sa), + sizeof (struct ipsec_proto), + ipsec_debug_attribute, + ipsec_delete_spi, + ipsec_exchange_script, + ipsec_finalize_exchange, + ipsec_free_exchange_data, + ipsec_free_proto_data, + ipsec_free_sa_data, + ipsec_get_keystate, + ipsec_get_spi, + ipsec_is_attribute_incompatible, + ipsec_setup_situation, + ipsec_situation_size, + ipsec_spi_size, + ipsec_validate_attribute, + ipsec_validate_exchange, + ipsec_validate_id_information, + ipsec_validate_key_information, + ipsec_validate_notification, + ipsec_validate_proto, + ipsec_validate_situation, + ipsec_validate_transform_id, + ipsec_initiator, + ipsec_responder +}; + +int16_t script_quick_mode[] = { + ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */ + ISAKMP_PAYLOAD_SA, + ISAKMP_PAYLOAD_NONCE, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_HASH, /* Responder -> initiator. */ + ISAKMP_PAYLOAD_SA, + ISAKMP_PAYLOAD_NONCE, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */ + EXCHANGE_SCRIPT_END +}; + +int16_t script_new_group_mode[] = { + ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */ + ISAKMP_PAYLOAD_SA, + EXCHANGE_SCRIPT_SWITCH, + ISAKMP_PAYLOAD_HASH, /* Responder -> initiator. */ + ISAKMP_PAYLOAD_SA, + EXCHANGE_SCRIPT_END +}; + +static void +ipsec_finalize_exchange (struct message *msg) +{ + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *sa; + struct proto *proto, *last_proto = 0; + int initiator = exchange->initiator; + struct timeval expiration; + + switch (exchange->phase) + { + case 1: + switch (exchange->type) + { + case ISAKMP_EXCH_ID_PROT: + case ISAKMP_EXCH_AGGRESSIVE: + isa->hash = ie->hash->type; + isa->prf_type = ie->prf_type; + isa->skeyid_len = ie->skeyid_len; + isa->skeyid_d = ie->skeyid_d; + isa->skeyid_a = ie->skeyid_a; + /* Prevents early free of SKEYID_*. */ + ie->skeyid_a = ie->skeyid_d = 0; + break; + } + + /* If a lifetime was negotiated sutup the death timer. */ + if (isakmp_sa->seconds) + { + gettimeofday(&expiration, 0); + expiration.tv_sec += isakmp_sa->seconds; + isakmp_sa->death = timer_add_event ("sa_rekey_p1", + (void (*) (void *))sa_rekey_p1, + isakmp_sa, &expiration); + if (!isakmp_sa->death) + { + /* If we don't give up we might start leaking... */ + sa_delete (isakmp_sa, 1); + return; + } + } + break; + + case 2: + switch (exchange->type) + { + case IKE_EXCH_QUICK_MODE: + /* + * Tell the application(s) about the SPIs and key material. + */ + for (sa = TAILQ_FIRST (&exchange->sa_list); sa; + sa = TAILQ_NEXT (sa, next)) + { + for (proto = TAILQ_FIRST (&sa->protos), last_proto = 0; proto; + proto = TAILQ_NEXT (proto, link)) + { + if (sysdep_ipsec_set_spi (sa, proto, 0, initiator) + || (last_proto + && sysdep_ipsec_group_spis (sa, last_proto, proto, + 0)) + || sysdep_ipsec_set_spi (sa, proto, 1, initiator) + || (last_proto + && sysdep_ipsec_group_spis (sa, last_proto, proto, + 1))) + /* XXX Tear down this exchange. */ + return; + last_proto = proto; + } + if (sysdep_ipsec_enable_spi (sa, initiator)) + /* XXX Tear down this exchange. */ + return; + } + break; + } + } +} + +static void +ipsec_free_exchange_data (void *vie) +{ + struct ipsec_exch *ie = vie; + + if (ie->sa_i_b) + free (ie->sa_i_b); + if (ie->g_xi) + free (ie->g_xi); + if (ie->g_xr) + free (ie->g_xr); + if (ie->g_xy) + free (ie->g_xy); + if (ie->skeyid) + free (ie->skeyid); + if (ie->skeyid_d) + free (ie->skeyid_d); + if (ie->skeyid_a) + free (ie->skeyid_a); + if (ie->skeyid_e) + free (ie->skeyid_e); + if (ie->hash_i) + free (ie->hash_i); + if (ie->hash_r) + free (ie->hash_r); +} + +static void +ipsec_free_sa_data (void *visa) +{ + struct ipsec_sa *isa = visa; + + if (isa->skeyid_a) + free (isa->skeyid_a); + if (isa->skeyid_d) + free (isa->skeyid_d); +} + +static void +ipsec_free_proto_data (void *viproto) +{ + struct ipsec_proto *iproto = viproto; + int i; + + for (i = 0; i < 2; i++) + if (iproto->keymat[i]) + free (iproto->keymat[i]); +} + +static u_int16_t * +ipsec_exchange_script (u_int8_t type) +{ + switch (type) + { + case IKE_EXCH_QUICK_MODE: + return script_quick_mode; + case IKE_EXCH_NEW_GROUP_MODE: + return script_new_group_mode; + } + return 0; +} + +/* Requires doi_init to already have been called. */ +void +ipsec_init () +{ + doi_register (&ipsec_doi); +} + +/* Given a message MSG, return a suitable IV (or rather keystate). */ +static struct keystate * +ipsec_get_keystate (struct message *msg) +{ + struct keystate *ks; + struct hash *hash; + + /* If we have already have an IV, use it. */ + if (msg->exchange && msg->exchange->keystate) + return msg->exchange->keystate; + + /* + * For phase 2 when no SA yet is setup we need to hash the IV used by + * the ISAKMP SA concatenated with the message ID, and use that as an + * IV for further cryptographic operations. + */ + ks = crypto_clone_keystate (msg->isakmp_sa->keystate); + if (!ks) + return 0; + + hash = hash_get (((struct ipsec_sa *)msg->isakmp_sa->data)->hash); + hash->Init (hash->ctx); + log_debug_buf (LOG_CRYPTO, 80, "ipsec_get_keystate: final phase 1 IV", + ks->riv, ks->xf->blocksize); + hash->Update (hash->ctx, ks->riv, ks->xf->blocksize); + log_debug_buf (LOG_CRYPTO, 80, "ipsec_get_keystate: message ID", + ((u_int8_t *)msg->iov[0].iov_base) + + ISAKMP_HDR_MESSAGE_ID_OFF, + ISAKMP_HDR_MESSAGE_ID_LEN); + hash->Update (hash->ctx, + ((u_int8_t *)msg->iov[0].iov_base) + ISAKMP_HDR_MESSAGE_ID_OFF, + ISAKMP_HDR_MESSAGE_ID_LEN); + hash->Final (hash->digest, hash->ctx); + crypto_init_iv (ks, hash->digest, ks->xf->blocksize); + log_debug_buf (LOG_CRYPTO, 80, "ipsec_get_keystate: phase 2 IV", + hash->digest, ks->xf->blocksize); + return ks; +} + +static void +ipsec_setup_situation (u_int8_t *buf) +{ + SET_IPSEC_SIT_SIT (buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); +} + +static size_t +ipsec_situation_size (void) +{ + return IPSEC_SIT_SIT_LEN; +} + +static u_int8_t +ipsec_spi_size (u_int8_t proto) +{ + return IPSEC_SPI_SIZE; +} + +static int +ipsec_validate_attribute (u_int16_t type, u_int8_t *value, u_int16_t len, + void *vmsg) +{ + struct message *msg = vmsg; + + if ((msg->exchange->phase == 1 + && (type < IKE_ATTR_ENCRYPTION_ALGORITHM + || type > IKE_ATTR_GROUP_ORDER)) + || (msg->exchange->phase == 2 + && (type < IPSEC_ATTR_SA_LIFE_TYPE + || type > IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM))) + return -1; + return 0; +} + +static int +ipsec_validate_exchange (u_int8_t exch) +{ + return exch != IKE_EXCH_QUICK_MODE && exch != IKE_EXCH_NEW_GROUP_MODE; +} + +static int +ipsec_validate_id_information (u_int8_t type, u_int8_t *extra, u_int8_t *buf, + size_t sz, struct exchange *exchange) +{ + u_int8_t proto = GET_IPSEC_ID_PROTO (extra); + u_int16_t port = GET_IPSEC_ID_PORT (extra); + + log_debug (LOG_MESSAGE, 0, + "ipsec_validate_id_information: proto %d port %d type %d", + proto, port, type); + if (type < IPSEC_ID_IPV4_ADDR || type > IPSEC_ID_KEY_ID) + return -1; + + if (exchange->phase == 1 + && (proto != IPPROTO_UDP || port != UDP_DEFAULT_PORT) + && (proto != 0 || port != 0)) + { +/* XXX SSH's ISAKMP tester fails this test (proto 17 - port 0). */ +#ifdef notyet + return -1; +#else + log_print ("ipsec_validate_id_information: " + "dubious ID information accepted"); +#endif + } + + /* XXX More checks? */ + + return 0; +} + +static int +ipsec_validate_key_information (u_int8_t *buf, size_t sz) +{ + /* XXX Not implemented yet. */ + return 0; +} + +static int +ipsec_validate_notification (u_int16_t type) +{ + return type < IPSEC_NOTIFY_RESPONDER_LIFETIME + || type > IPSEC_NOTIFY_INITIAL_CONTACT ? -1 : 0; +} + +static int +ipsec_validate_proto (u_int8_t proto) +{ + return proto < IPSEC_PROTO_IPSEC_AH || proto > IPSEC_PROTO_IPCOMP ? -1 : 0; +} + +static int +ipsec_validate_situation (u_int8_t *buf, size_t *sz) +{ + int sit = GET_IPSEC_SIT_SIT (buf); + int off; + + if (sit & (IPSEC_SIT_SECRECY | IPSEC_SIT_INTEGRITY)) + { + /* + * XXX All the roundups below, round up to 32 bit boundaries given + * that the situation field is aligned. This is not necessarily so, + * but I interpret the drafts as this is like this they want it. + */ + off = ROUNDUP_32 (GET_IPSEC_SIT_SECRECY_LENGTH (buf)); + off += ROUNDUP_32 (GET_IPSEC_SIT_SECRECY_CAT_LENGTH (buf + off)); + off += ROUNDUP_32 (GET_IPSEC_SIT_INTEGRITY_LENGTH (buf + off)); + off += ROUNDUP_32 (GET_IPSEC_SIT_INTEGRITY_CAT_LENGTH (buf + off)); + *sz = off + IPSEC_SIT_SZ; + } + else + *sz = IPSEC_SIT_SIT_LEN; + + /* Currently only "identity only" situations are supported. */ +#ifdef notdef + return + sit & ~(IPSEC_SIT_IDENTITY_ONLY | IPSEC_SIT_SECRECY | IPSEC_SIT_INTEGRITY); +#else + return sit & ~IPSEC_SIT_IDENTITY_ONLY; +#endif + return 1; + return 0; +} + +static int +ipsec_validate_transform_id (u_int8_t proto, u_int8_t transform_id) +{ + switch (proto) + { + /* + * As no unexpected protocols can occur, we just tie the default case + * to the first case, in orer to silence a GCC warning. + */ + default: + case ISAKMP_PROTO_ISAKMP: + return transform_id != IPSEC_TRANSFORM_KEY_IKE; + case IPSEC_PROTO_IPSEC_AH: + return + transform_id < IPSEC_AH_MD5 || transform_id > IPSEC_AH_DES ? -1 : 0; + case IPSEC_PROTO_IPSEC_ESP: + return transform_id < IPSEC_ESP_DES_IV64 + || transform_id > IPSEC_ESP_NULL ? -1 : 0; + case IPSEC_PROTO_IPCOMP: + return transform_id < IPSEC_IPCOMP_OUI + || transform_id > IPSEC_IPCOMP_V42BIS ? -1 : 0; + } +} + +static int +ipsec_initiator (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + int (**script) (struct message *msg) = 0; + + /* XXX Mostly not implemented yet. */ + + /* Check that the SA is coherent with the IKE rules. */ + if ((exchange->phase == 1 && exchange->type != ISAKMP_EXCH_ID_PROT + && exchange->type != ISAKMP_EXCH_INFO) + || (exchange->phase == 2 && exchange->type == ISAKMP_EXCH_ID_PROT)) + { + log_print ("ipsec_initiator: unsupported exchange type %d in phase %d", + exchange->type, exchange->phase); + return -1; + } + + switch (exchange->type) + { + case ISAKMP_EXCH_BASE: + break; + case ISAKMP_EXCH_ID_PROT: + script = ike_main_mode_initiator; + break; + case ISAKMP_EXCH_AUTH_ONLY: + log_print ("ipsec_initiator: unuspported exchange type %d", + exchange->type); + return -1; + case ISAKMP_EXCH_AGGRESSIVE: + break; + case ISAKMP_EXCH_INFO: + message_send_info (msg); + break; + case IKE_EXCH_QUICK_MODE: + script = ike_quick_mode_initiator; + break; + case IKE_EXCH_NEW_GROUP_MODE: + break; + } + + /* Run the script code for this step. */ + if (script) + return script[exchange->step] (msg); + + return 0; +} + +static int +ipsec_responder (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + int (**script) (struct message *msg) = 0; + + /* Check that a new exchange is coherent with the IKE rules. */ + if (exchange->step == 0 + && ((exchange->phase == 1 && exchange->type != ISAKMP_EXCH_ID_PROT + && exchange->type != ISAKMP_EXCH_INFO) + || (exchange->phase == 2 && exchange->type == ISAKMP_EXCH_ID_PROT))) + { + message_drop (msg, ISAKMP_NOTIFY_UNSUPPORTED_EXCHANGE_TYPE, 0, 0, 0); + return -1; + } + + log_debug (LOG_MISC, 30, + "ipsec_responder: phase %d exchange %d step %d", exchange->phase, + exchange->type, exchange->step); + switch (exchange->type) + { + case ISAKMP_EXCH_BASE: + case ISAKMP_EXCH_AUTH_ONLY: + message_drop (msg, ISAKMP_NOTIFY_UNSUPPORTED_EXCHANGE_TYPE, 0, 0, 0); + return -1; + + case ISAKMP_EXCH_ID_PROT: + script = ike_main_mode_responder; + break; + + case ISAKMP_EXCH_AGGRESSIVE: + /* XXX Not implemented yet. */ + break; + + case ISAKMP_EXCH_INFO: + /* XXX Not implemented yet. */ + break; + + case IKE_EXCH_QUICK_MODE: + script = ike_quick_mode_responder; + break; + + case IKE_EXCH_NEW_GROUP_MODE: + /* XXX Not implemented yet. */ + break; + } + + /* Run the script code for this step. */ + if (script) + return script[exchange->step] (msg); + + /* + * XXX So far we don't accept any proposals for exchanges we don't support. + */ + if (TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA])) + { + message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 0, 0); + return -1; + } + return 0; +} + +static enum hashes from_ike_hash (u_int16_t hash) +{ + switch (hash) + { + case IKE_HASH_MD5: + return HASH_MD5; + case IKE_HASH_SHA: + return HASH_SHA1; + } + return -1; +} + +static enum transform from_ike_crypto (u_int16_t crypto) +{ + /* Coincidentally this is the null operation :-) */ + return crypto; +} + +int +ipsec_is_attribute_incompatible (u_int16_t type, u_int8_t *value, + u_int16_t len, void *vmsg) +{ + struct message *msg = vmsg; + + if (msg->exchange->phase == 1) + { + switch (type) + { + case IKE_ATTR_ENCRYPTION_ALGORITHM: + return !crypto_get (from_ike_crypto (decode_16 (value))); + case IKE_ATTR_HASH_ALGORITHM: + return !hash_get (from_ike_hash (decode_16 (value))); + case IKE_ATTR_AUTHENTICATION_METHOD: + return !ike_auth_get (decode_16 (value)); + case IKE_ATTR_GROUP_DESCRIPTION: + return decode_16 (value) < IKE_GROUP_DESC_MODP_768 + || decode_16 (value) > IKE_GROUP_DESC_EC2N_185; + case IKE_ATTR_GROUP_TYPE: + return 1; + case IKE_ATTR_GROUP_PRIME: + return 1; + case IKE_ATTR_GROUP_GENERATOR_1: + return 1; + case IKE_ATTR_GROUP_GENERATOR_2: + return 1; + case IKE_ATTR_GROUP_CURVE_A: + return 1; + case IKE_ATTR_GROUP_CURVE_B: + return 1; + case IKE_ATTR_LIFE_TYPE: + return decode_16 (value) < IKE_DURATION_SECONDS + || decode_16 (value) > IKE_DURATION_KILOBYTES; + case IKE_ATTR_LIFE_DURATION: + return 0; + case IKE_ATTR_PRF: + return 1; + case IKE_ATTR_KEY_LENGTH: + /* + * Our crypto routines only allows key-lengths which are multiples + * of an octet. + */ + return decode_16 (value) % 8 != 0; + case IKE_ATTR_FIELD_SIZE: + return 1; + case IKE_ATTR_GROUP_ORDER: + return 1; + } + } + else + { + switch (type) + { + case IPSEC_ATTR_SA_LIFE_TYPE: + return decode_16 (value) < IPSEC_DURATION_SECONDS + || decode_16 (value) > IPSEC_DURATION_KILOBYTES; + case IPSEC_ATTR_SA_LIFE_DURATION: + return 0; + case IPSEC_ATTR_GROUP_DESCRIPTION: + return decode_16 (value) < IKE_GROUP_DESC_MODP_768 + || decode_16 (value) > IKE_GROUP_DESC_EC2N_185; + case IPSEC_ATTR_ENCAPSULATION_MODE: + return decode_16 (value) < IPSEC_ENCAP_TUNNEL + || decode_16 (value) > IPSEC_ENCAP_TRANSPORT; + case IPSEC_ATTR_AUTHENTICATION_ALGORITHM: + return decode_16 (value) < IPSEC_AUTH_HMAC_MD5 + || decode_16 (value) > IPSEC_AUTH_KPDK; + case IPSEC_ATTR_KEY_LENGTH: + return 1; + case IPSEC_ATTR_KEY_ROUNDS: + return 1; + case IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE: + return 1; + case IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM: + return 1; + } + } + /* XXX Silence gcc. */ + return 1; +} + +int +ipsec_debug_attribute (u_int16_t type, u_int8_t *value, u_int16_t len, + void *vmsg) +{ + struct message *msg = vmsg; + char val[20]; + + /* XXX Transient solution. */ + if (len == 2) + sprintf (val, "%d", decode_16 (value)); + else if (len == 4) + sprintf (val, "%d", decode_32 (value)); + else + sprintf (val, "unrepresentable"); + + log_debug (LOG_MESSAGE, 50, "Attribute %s value %s", + constant_name (msg->exchange->phase == 1 + ? ike_attr_cst : ipsec_attr_cst, type), + val); + return 0; +} + +int +ipsec_decode_attribute (u_int16_t type, u_int8_t *value, u_int16_t len, + void *vida) +{ + struct ipsec_decode_arg *ida = vida; + struct message *msg = ida->msg; + struct sa *sa = ida->sa; + struct ipsec_sa *isa = sa->data; + struct proto *proto = ida->proto; + struct ipsec_proto *iproto = proto->data; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + static int lifetype = 0; + + if (exchange->phase == 1) + { + switch (type) + { + case IKE_ATTR_ENCRYPTION_ALGORITHM: + /* XXX Errors possible? */ + exchange->crypto = crypto_get (from_ike_crypto (decode_16 (value))); + break; + case IKE_ATTR_HASH_ALGORITHM: + /* XXX Errors possible? */ + ie->hash = hash_get (from_ike_hash (decode_16 (value))); + break; + case IKE_ATTR_AUTHENTICATION_METHOD: + /* XXX Errors possible? */ + ie->ike_auth = ike_auth_get (decode_16 (value)); + break; + case IKE_ATTR_GROUP_DESCRIPTION: + /* XXX Errors possible? */ + ie->group = group_get (decode_16 (value)); + break; + case IKE_ATTR_GROUP_TYPE: + break; + case IKE_ATTR_GROUP_PRIME: + break; + case IKE_ATTR_GROUP_GENERATOR_1: + break; + case IKE_ATTR_GROUP_GENERATOR_2: + break; + case IKE_ATTR_GROUP_CURVE_A: + break; + case IKE_ATTR_GROUP_CURVE_B: + break; + case IKE_ATTR_LIFE_TYPE: + lifetype = decode_16 (value); + return 0; + case IKE_ATTR_LIFE_DURATION: + switch (lifetype) + { + case IKE_DURATION_SECONDS: + switch (len) + { + case 2: + sa->seconds = decode_16 (value); + break; + case 4: + sa->seconds = decode_32 (value); + break; + default: + /* XXX Log. */ + } + break; + case IKE_DURATION_KILOBYTES: + switch (len) + { + case 2: + sa->kilobytes = decode_16 (value); + break; + case 4: + sa->kilobytes = decode_32 (value); + break; + default: + /* XXX Log. */ + } + break; + default: + /* XXX Log! */ + } + break; + case IKE_ATTR_PRF: + break; + case IKE_ATTR_KEY_LENGTH: + exchange->key_length = decode_16 (value) / 8; + break; + case IKE_ATTR_FIELD_SIZE: + break; + case IKE_ATTR_GROUP_ORDER: + break; + } + } + else + { + switch (type) + { + case IPSEC_ATTR_SA_LIFE_TYPE: + lifetype = decode_16 (value); + return 0; + case IPSEC_ATTR_SA_LIFE_DURATION: + switch (lifetype) + { + case IPSEC_DURATION_SECONDS: + switch (len) + { + case 2: + sa->seconds = decode_16 (value); + break; + case 4: + sa->seconds = decode_32 (value); + break; + default: + /* XXX Log. */ + } + break; + case IPSEC_DURATION_KILOBYTES: + switch (len) + { + case 2: + sa->kilobytes = decode_16 (value); + break; + case 4: + sa->kilobytes = decode_32 (value); + break; + default: + /* XXX Log. */ + } + break; + default: + /* XXX Log! */ + } + break; + case IPSEC_ATTR_GROUP_DESCRIPTION: + isa->group_desc = decode_16 (value); + break; + case IPSEC_ATTR_ENCAPSULATION_MODE: + /* XXX Multiple protocols must have same encapsulation mode, no? */ + iproto->encap_mode = decode_16 (value); + break; + case IPSEC_ATTR_AUTHENTICATION_ALGORITHM: + iproto->auth = decode_16 (value); + break; + case IPSEC_ATTR_KEY_LENGTH: + iproto->keylen = decode_16 (value); + break; + case IPSEC_ATTR_KEY_ROUNDS: + iproto->keyrounds = decode_16 (value); + break; + case IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE: + break; + case IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM: + break; + } + } + lifetype = 0; + return 0; +} + +/* + * Walk over the attributes of the transform payload found in BUF, and + * fill out the fields of the SA attached to MSG. Also mark the SA as + * processed. + */ +void +ipsec_decode_transform (struct message *msg, struct sa *sa, + struct proto *proto, u_int8_t *buf) +{ + struct ipsec_exch *ie = msg->exchange->data; + struct ipsec_decode_arg ida; + + log_debug (LOG_MISC, 20, "ipsec_decode_transform: transform %d chosen", + GET_ISAKMP_TRANSFORM_NO (buf)); + + ida.msg = msg; + ida.sa = sa; + ida.proto = proto; + + /* The default IKE lifetime is 8 hours. */ + if (sa->phase == 1) + sa->seconds = 28800; + + /* Extract the attributes and stuff them into the SA. */ + attribute_map (buf + ISAKMP_TRANSFORM_SA_ATTRS_OFF, + GET_ISAKMP_GEN_LENGTH (buf) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, + ipsec_decode_attribute, &ida); + + /* + * If no pseudo-random function was negotiated, it's HMAC. + * XXX As PRF_HMAC currently is zero, this is a no-op. + */ + if (!ie->prf_type) + ie->prf_type = PRF_HMAC; +} + +static void +ipsec_delete_spi (struct sa *sa, struct proto *proto, int initiator) +{ + if (sa->phase == 1) + return; + /* XXX Error handling? Is it interesting? */ + sysdep_ipsec_delete_spi (sa, proto, initiator); +} + +static int +ipsec_g_x (struct message *msg, int peer, u_int8_t *buf) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + u_int8_t **g_x; + int initiator = exchange->initiator ^ peer; + char header[32]; + + g_x = initiator ? &ie->g_xi : &ie->g_xr; + *g_x = malloc (ie->g_x_len); + if (!*g_x) + return -1; + memcpy (*g_x, buf, ie->g_x_len); + snprintf (header, 32, "ipsec_g_x: g^x%c", initiator ? 'i' : 'r'); + log_debug_buf (LOG_MISC, 80, header, *g_x, ie->g_x_len); + return 0; +} + +/* Generate our DH value. */ +int +ipsec_gen_g_x (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + u_int8_t *buf; + + buf = malloc (ISAKMP_KE_SZ + ie->g_x_len); + if (!buf) + return -1; + + if (message_add_payload (msg, ISAKMP_PAYLOAD_KEY_EXCH, buf, + ISAKMP_KE_SZ + ie->g_x_len, 1)) + { + free (buf); + return -1; + } + + dh_create_exchange (ie->group, buf + ISAKMP_KE_DATA_OFF); + return ipsec_g_x (msg, 0, buf + ISAKMP_KE_DATA_OFF); +} + +/* Save the peer's DH value. */ +int +ipsec_save_g_x (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct payload *kep; + + kep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]); + kep->flags |= PL_MARK; + ie->g_x_len = GET_ISAKMP_GEN_LENGTH (kep->p) - ISAKMP_KE_DATA_OFF; + + /* Check that the given length matches the group's expectancy. */ + if (ie->g_x_len != dh_getlen (ie->group)) + { + /* XXX Is this a good notify type? */ + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 0); + return -1; + } + + return ipsec_g_x (msg, 1, kep->p + ISAKMP_KE_DATA_OFF); +} + +/* + * Get a SPI for PROTO and the transport MSG passed over. Store the + * size where SZ points. NB! A zero return is OK if *SZ is zero. + */ +static u_int8_t * +ipsec_get_spi (size_t *sz, u_int8_t proto, struct message *msg) +{ + struct sockaddr *dst; + int dstlen; + struct transport *transport = msg->transport; + + if (msg->exchange->phase == 1) + { + *sz = 0; + return 0; + } + else + { + /* We are the destination in the SA we want a SPI for. */ + transport->vtbl->get_src (transport, &dst, &dstlen); + return sysdep_ipsec_get_spi (sz, proto, dst, dstlen); + } +} + +int +ipsec_esp_enckeylength (struct proto *proto) +{ + struct ipsec_proto *iproto = proto->data; + + /* Compute the keylength to use. */ + switch (proto->id) + { + case IPSEC_ESP_DES: + case IPSEC_ESP_DES_IV32: + case IPSEC_ESP_DES_IV64: + return 8; + case IPSEC_ESP_3DES: + return 24; + default: + return iproto->keylen / 8; + } +} + +int +ipsec_esp_authkeylength (struct proto *proto) +{ + struct ipsec_proto *iproto = proto->data; + + switch (iproto->auth) + { + case IPSEC_AUTH_HMAC_MD5: + return 16; + case IPSEC_AUTH_HMAC_SHA: + return 20; + default: + return 0; + } +} + +int +ipsec_ah_keylength (struct proto *proto) +{ + switch (proto->id) + { + case IPSEC_AH_MD5: + return 16; + case IPSEC_AH_SHA: + return 20; + default: + return -1; + } +} + +int +ipsec_keymat_length (struct proto *proto) +{ + switch (proto->proto) + { + case IPSEC_PROTO_IPSEC_ESP: + return ipsec_esp_enckeylength (proto) + ipsec_esp_authkeylength (proto); + case IPSEC_PROTO_IPSEC_AH: + return ipsec_ah_keylength (proto); + default: + return -1; + } +} diff --git a/sbin/isakmpd/ipsec.h b/sbin/isakmpd/ipsec.h new file mode 100644 index 00000000000..f8f9bb254c6 --- /dev/null +++ b/sbin/isakmpd/ipsec.h @@ -0,0 +1,123 @@ +/* $Id: ipsec.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _IPSEC_H_ +#define _IPSEC_H_ + +#include "ipsec_doi.h" + +struct group; +struct hash; +struct ike_auth; +struct message; +struct proto; +struct sa; + +/* + * IPSEC-specific data to be linked into the exchange struct. + * XXX Should probably be two different structs, one for phase 1 and one + * for phase 2 parameters. + */ +struct ipsec_exch { + struct hash *hash; + struct ike_auth *ike_auth; + struct group *group; + u_int16_t prf_type; + + /* + * A copy of the initiator SA payload body for later computation of hashes. + * Phase 1 only. + */ + size_t sa_i_b_len; + u_int8_t *sa_i_b; + + /* Diffie-Hellman values. */ + size_t g_x_len; + u_int8_t *g_xi; + u_int8_t *g_xr; + u_int8_t* g_xy; + + /* SKEYIDs. XXX Phase 1 only? */ + size_t skeyid_len; + u_int8_t *skeyid; + u_int8_t *skeyid_d; + u_int8_t *skeyid_a; + u_int8_t *skeyid_e; + + /* HASH_I & HASH_R. XXX Do these need to be saved here? */ + u_int8_t *hash_i; + u_int8_t *hash_r; + + /* KEYMAT */ + size_t keymat_len; +}; + +struct ipsec_sa { + /* Phase 1. */ + u_int8_t hash; + size_t skeyid_len; + u_int8_t *skeyid_d; + u_int8_t *skeyid_a; + u_int16_t prf_type; + + /* Phase 2. */ + u_int16_t group_desc; +}; + +struct ipsec_proto { + /* Phase 2. */ + u_int16_t encap_mode; + u_int16_t auth; + u_int16_t keylen; + u_int16_t keyrounds; + + /* KEYMAT */ + u_int8_t *keymat[2]; +}; + +extern int ipsec_ah_keylength (struct proto *); +extern int ipsec_decode_attribute (u_int16_t, u_int8_t *, u_int16_t, void *); +extern void ipsec_decode_transform (struct message *, struct sa *, + struct proto *, u_int8_t *); +extern int ipsec_esp_authkeylength (struct proto *); +extern int ipsec_esp_enckeylength (struct proto *); +extern int ipsec_gen_g_x (struct message *); +extern void ipsec_init (void); +extern int ipsec_is_attribute_incompatible (u_int16_t, u_int8_t *, u_int16_t, + void *); +extern int ipsec_keymat_length (struct proto *); +extern int ipsec_save_g_x (struct message *); + +#endif /* _IPSEC_H_ */ diff --git a/sbin/isakmpd/ipsec_doi.h b/sbin/isakmpd/ipsec_doi.h new file mode 100644 index 00000000000..dc62e72b4c5 --- /dev/null +++ b/sbin/isakmpd/ipsec_doi.h @@ -0,0 +1,45 @@ +/* $Id: ipsec_doi.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _IPSEC_DOI_H_ +#define _IPSEC_DOI_H_ + +#include "ipsec_fld.h" +#include "ipsec_num.h" + +/* The SPI size of all IPSEC protocols. XXX Correct? */ +#define IPSEC_SPI_SIZE 4 + +#endif /* _IPSEC_DOI_H_ */ diff --git a/sbin/isakmpd/ipsec_fld.fld b/sbin/isakmpd/ipsec_fld.fld new file mode 100644 index 00000000000..36dd47b6e94 --- /dev/null +++ b/sbin/isakmpd/ipsec_fld.fld @@ -0,0 +1,64 @@ +# $Id: ipsec_fld.fld,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +# XXX There are num-declared fields below that really are csts. + +# IPSEC's situation field's subdivision. +IPSEC_SIT + SIT mask 4 ipsec_sit_cst + LABELED_DOMAIN_ID num 4 + SECRECY_LENGTH num 2 + RESERVED_1 ign 2 +# The following fields' offsets need the secrecy length added + 32bit +# alignment. + SECRECY_CAT_LENGTH num 2 + RESERVED_2 ign 2 +# The following fields' offsets need the secrecy cat length added + 32bit +# alignment on top of the aforementioned offset. + INTEGRITY_LENGTH num 2 + RESERVED_3 ign 2 +# The following fields' offsets need the integrity length added + 32bit +# alignment on top of the aforementioned offset. + INTEGRITY_CAT_LENGTH num 2 + RESERVED_4 ign 2 +# The IPSEC_SIT record's length need the integrity cat length added + 32bit +# alignment on top of the aforementioned offset. +. + +# IPSEC's layout of the identification payload's DOI data field. +IPSEC_ID + PROTO num 1 + PORT num 2 +. diff --git a/sbin/isakmpd/ipsec_num.cst b/sbin/isakmpd/ipsec_num.cst new file mode 100644 index 00000000000..3a98db038bd --- /dev/null +++ b/sbin/isakmpd/ipsec_num.cst @@ -0,0 +1,225 @@ +# $Id: ipsec_num.cst,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +# XXX Please fill in references to the drafts, chapter & verse for each +# constant group below. + +# IPSEC DOI Identifier. +IPSEC_DOI + IPSEC 1 +. + +# IPSEC SA attributes +IPSEC_ATTR + SA_LIFE_TYPE 1 + SA_LIFE_DURATION 2 + GROUP_DESCRIPTION 3 + ENCAPSULATION_MODE 4 + AUTHENTICATION_ALGORITHM 5 + KEY_LENGTH 6 + KEY_ROUNDS 7 + COMPRESS_DICTIONARY_SIZE 8 + COMPRESS_PRIVATE_ALGORITHM 9 +. + +# IPSEC SA duration. +IPSEC_DURATION + SECONDS 1 + KILOBYTES 2 +. + +# IPSEC encapsulation mode. +IPSEC_ENCAP + TUNNEL 1 + TRANSPORT 2 +. + +# IPSEC authentication algorithm. +IPSEC_AUTH + HMAC_MD5 1 + HMAC_SHA 2 + DES_MAC 3 + KPDK 4 +. + +# IPSEC ID types. +IPSEC_ID + IPV4_ADDR 1 + FQDN 2 + USER_FQDN 3 + IPV4_ADDR_SUBNET 4 + IPV6_ADDR 5 + IPV6_ADDR_SUBNET 6 + IPV4_RANGE 7 + IPV6_RANGE 8 + DER_ASN1_DN 9 + DER_ASN1_GN 10 + KEY_ID 11 +. + +# IKE SA attributes +IKE_ATTR + ENCRYPTION_ALGORITHM 1 + HASH_ALGORITHM 2 + AUTHENTICATION_METHOD 3 + GROUP_DESCRIPTION 4 + GROUP_TYPE 5 + GROUP_PRIME 6 + GROUP_GENERATOR_1 7 + GROUP_GENERATOR_2 8 + GROUP_CURVE_A 9 + GROUP_CURVE_B 10 + LIFE_TYPE 11 + LIFE_DURATION 12 + PRF 13 + KEY_LENGTH 14 + FIELD_SIZE 15 + GROUP_ORDER 16 +. + +# XXX Fill in reserved ranges for the attributes below. + +# IKE encryption algorithm. +IKE_ENCRYPT + DES_CBC 1 + IDEA_CBC 2 + BLOWFISH_CBC 3 + RC5_R16_B64_CBC 4 + 3DES_CBC 5 + CAST_CBC 6 +. + +# IKE hash algorithm. +IKE_HASH + MD5 1 + SHA 2 + TIGER 3 +. + +# IKE authentication method. +IKE_AUTH + PRE_SHARED 1 + DSS 2 + RSA_SIG 3 + RSA_ENC 4 + RSA_ENC_REV 5 +. + +# IKE group description. +IKE_GROUP_DESC + MODP_768 1 + MODP_1024 2 + EC2N_155 3 + EC2N_185 4 +. + +# IKE Group type. +IKE_GROUP + MODP 1 + ECP 2 + EC2N 3 +. + +# IKE SA duration. +IKE_DURATION + SECONDS 1 + KILOBYTES 2 +. + +# IKE Pseudo random function. No defined so far. +IKE_PRF +. + +# IPSEC Situation bits. +IPSEC_SIT + IDENTITY_ONLY 1 + SECRECY 2 + INTEGRITY 4 +. + +# IPSEC security protocol IDs. +IPSEC_PROTO + IPSEC_AH 2 + IPSEC_ESP 3 + IPCOMP 4 +. + +# IPSEC ISAKMP transform IDs. +IPSEC_TRANSFORM + KEY_IKE 1 +. + +# IPSEC AH transform IDs. +IPSEC_AH + MD5 2 + SHA 3 + DES 4 +. + +# IPSEC ESP transform IDs. +IPSEC_ESP + DES_IV64 1 + DES 2 + 3DES 3 + RC5 4 + IDEA 5 + CAST 6 + BLOWFISH 7 + 3IDEA 8 + DES_IV32 9 + RC4 10 + NULL 11 +. + +# IPSEC IPCOMP transform IDs +IPSEC_IPCOMP + OUI 1 + DEFLATE 2 + LZS 3 + V42BIS 4 +. + +# IPSEC notify message types. +IPSEC_NOTIFY + RESPONDER_LIFETIME 24576 + REPLAY_STATUS 24577 + INITIAL_CONTACT 24578 +. + +# IKE exchange types. +IKE_EXCH + QUICK_MODE 32 + NEW_GROUP_MODE 33 +. diff --git a/sbin/isakmpd/isakmp.h b/sbin/isakmpd/isakmp.h new file mode 100644 index 00000000000..01adf8dabca --- /dev/null +++ b/sbin/isakmpd/isakmp.h @@ -0,0 +1,60 @@ +/* $Id: isakmp.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _ISAKMP_H_ +#define _ISAKMP_H_ + +#include "isakmp_fld.h" +#include "isakmp_num.h" + +/* IANA assigned port */ +#define UDP_DEFAULT_PORT 500 + +/* ISAKMP header extras defines */ +#define ISAKMP_HDR_COOKIES_OFF ISAKMP_HDR_ICOOKIE_OFF +#define ISAKMP_HDR_COOKIES_LEN (ISAKMP_HDR_ICOOKIE_LEN \ + + ISAKMP_HDR_ICOOKIE_LEN) + +/* ISAKMP attribute utilitiy macros. */ +#define ISAKMP_ATTR_FORMAT(x) ((x) >> 15) +#define ISAKMP_ATTR_TYPE(x) ((x) & 0x7fff) +#define ISAKMP_ATTR_MAKE(fmt, type) (((fmt) << 15) | (type)) + +/* Version number handling. */ +#define ISAKMP_VERSION_MAJOR(x) ((x) >> 4) +#define ISAKMP_VERSION_MINOR(x) ((x) & 0xf) +#define ISAKMP_VERSION_MAKE(maj, min) ((maj) << 4 | (min)) + +#endif /* _ISAKMP_H_ */ diff --git a/sbin/isakmpd/isakmp_doi.c b/sbin/isakmpd/isakmp_doi.c new file mode 100644 index 00000000000..6c87d8bcccd --- /dev/null +++ b/sbin/isakmpd/isakmp_doi.c @@ -0,0 +1,218 @@ +/* $Id: isakmp_doi.c,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * XXX This DOI is very fuzzily defined, and should perhaps be short-circuited + * to the IPSEC DOI instead. At the moment I will have it as its own DOI, + * as the ISAKMP architecture seems to imply it should be done like this. + */ + +#include <sys/types.h> + +#include "doi.h" +#include "exchange.h" +#include "isakmp.h" +#include "log.h" +#include "message.h" +#include "sa.h" +#include "util.h" + +static int isakmp_debug_attribute (u_int16_t, u_int8_t *, u_int16_t, void *); +static void isakmp_finalize_exchange (struct message *); +static struct keystate *isakmp_get_keystate (struct message *); +static int isakmp_initiator (struct message *); +static int isakmp_responder (struct message *); +static void isakmp_setup_situation (u_int8_t *); +static size_t isakmp_situation_size (void); +static u_int8_t isakmp_spi_size (u_int8_t); +static int isakmp_validate_attribute (u_int16_t, u_int8_t *, u_int16_t, + void *); +static int isakmp_validate_exchange (u_int8_t); +static int isakmp_validate_id_information (u_int8_t, u_int8_t *, u_int8_t *, + size_t, struct exchange *); +static int isakmp_validate_key_information (u_int8_t *, size_t); +static int isakmp_validate_notification (u_int16_t); +static int isakmp_validate_proto (u_int8_t); +static int isakmp_validate_situation (u_int8_t *, size_t *); +static int isakmp_validate_transform_id (u_int8_t, u_int8_t); + +static struct doi isakmp_doi = { + { 0 }, ISAKMP_DOI_ISAKMP, 0, 0, 0, + isakmp_debug_attribute, + 0, /* delete_spi not needed. */ + 0, /* exchange_script not needed. */ + isakmp_finalize_exchange, + 0, /* free_exchange_data not needed. */ + 0, /* free_proto_data not needed. */ + 0, /* free_sa_data not needed. */ + isakmp_get_keystate, + 0, /* get_spi not needed. */ + 0, /* XXX need maybe be filled-in. */ + isakmp_setup_situation, + isakmp_situation_size, + isakmp_spi_size, + isakmp_validate_attribute, + isakmp_validate_exchange, + isakmp_validate_id_information, + isakmp_validate_key_information, + isakmp_validate_notification, + isakmp_validate_proto, + isakmp_validate_situation, + isakmp_validate_transform_id, + isakmp_initiator, + isakmp_responder +}; + +/* Requires doi_init to already have been called. */ +void +isakmp_doi_init () +{ + doi_register (&isakmp_doi); +} + +int +isakmp_debug_attribute (u_int16_t type, u_int8_t *value, u_int16_t len, + void *vmsg) +{ + /* XXX Not implemented yet. */ + return 0; +} + +static void +isakmp_finalize_exchange (struct message *msg) +{ +} + +static struct keystate * +isakmp_get_keystate (struct message *msg) +{ + return 0; +} + +static void +isakmp_setup_situation (u_int8_t *buf) +{ + /* Nothing to do. */ +} + +static size_t +isakmp_situation_size (void) +{ + return 0; +} + +static u_int8_t +isakmp_spi_size (u_int8_t proto) +{ + /* One way to specify ISAKMP SPIs is to say they're zero-sized. */ + return 0; +} + +static int +isakmp_validate_attribute (u_int16_t type, u_int8_t *value, u_int16_t len, + void *vmsg) +{ + /* XXX Not implemented yet. */ + return -1; +} + +static int +isakmp_validate_exchange (u_int8_t exch) +{ + /* If we get here the exchange is invalid. */ + return -1; +} + +static int +isakmp_validate_id_information (u_int8_t type, u_int8_t *extra, u_int8_t *buf, + size_t sz, struct exchange *exchange) +{ + return zero_test (extra, ISAKMP_ID_DOI_DATA_LEN); +} + +static int +isakmp_validate_key_information (u_int8_t *buf, size_t sz) +{ + /* Nothing to do. */ + return 0; +} + +static int +isakmp_validate_notification (u_int16_t type) +{ + /* If we get here the message type is invalid. */ + return -1; +} + +static int +isakmp_validate_proto (u_int8_t proto) +{ + /* If we get here the protocol is invalid. */ + return -1; +} + +static int +isakmp_validate_situation (u_int8_t *buf, size_t *sz) +{ + /* There are no situations in the ISAKMP DOI. */ + *sz = 0; + return 0; +} + +static int +isakmp_validate_transform_id (u_int8_t proto, u_int8_t transform_id) +{ + /* XXX Not yet implemented. */ + return -1; +} + +static int +isakmp_initiator (struct message *msg) +{ + /* XXX Not implemented yet. */ + return 0; +} + +static int +isakmp_responder (struct message *msg) +{ + /* XXX So far we don't accept any proposals. */ + if (TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA])) + { + message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 0, 0); + return -1; + } + return 0; +} diff --git a/sbin/isakmpd/isakmp_doi.h b/sbin/isakmpd/isakmp_doi.h new file mode 100644 index 00000000000..a1cbf6de37a --- /dev/null +++ b/sbin/isakmpd/isakmp_doi.h @@ -0,0 +1,41 @@ +/* $Id: isakmp_doi.h,v 1.1 1998/11/15 00:03:48 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _ISAKMP_DOI_H_ +#define _ISAKMP_DOI_H_ + +extern void isakmp_doi_init (void); + +#endif /* _ISAKMP_DOI_H_ */ diff --git a/sbin/isakmpd/isakmp_fld.fld b/sbin/isakmpd/isakmp_fld.fld new file mode 100644 index 00000000000..19e2e36425f --- /dev/null +++ b/sbin/isakmpd/isakmp_fld.fld @@ -0,0 +1,148 @@ +# $Id: isakmp_fld.fld,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +# XXX There are num-declared fields below that really are csts. + +# The ISAKMP message header. +ISAKMP_HDR +# XXX I want a way to specify COOKIES as an overlay of ICOOKIE + RCOOKIE + ICOOKIE raw 8 + RCOOKIE raw 8 + NEXT_PAYLOAD cst 1 isakmp_payload_cst + VERSION num 1 + EXCH_TYPE cst 1 ike_exch_cst,isakmp_exch_cst + FLAGS mask 1 isakmp_flags_cst + MESSAGE_ID raw 4 + LENGTH num 4 +. + +# Generic payload header. +ISAKMP_GEN + NEXT_PAYLOAD cst 1 isakmp_payload_cst + RESERVED ign 1 + LENGTH num 2 +. + +# ISAKMP data attributes +ISAKMP_ATTR + TYPE num 2 + LENGTH_VALUE num 2 + VALUE raw +. + +# Security association payload. +ISAKMP_SA : ISAKMP_GEN + DOI num 4 isakmp_doi_cst,ipsec_doi_cst + SIT raw +. + +# Proposal payload. +ISAKMP_PROP : ISAKMP_GEN + NO num 1 + PROTO cst 1 isakmp_proto_cst,ipsec_proto_cst + SPI_SZ num 1 + NTRANSFORMS num 1 + SPI raw +. + +# Transform payload. +ISAKMP_TRANSFORM : ISAKMP_GEN + NO num 1 + ID num 1 + RESERVED ign 2 + SA_ATTRS raw +. + +# Key exchange payload. +ISAKMP_KE : ISAKMP_GEN + DATA raw +. + +# Identification payload. +ISAKMP_ID : ISAKMP_GEN + TYPE num 1 + DOI_DATA raw 3 + DATA raw +. + +# Certificate payload. +ISAKMP_CERT : ISAKMP_GEN + ENCODING cst 1 isakmp_certenc_cst + DATA raw +. + +# Certificate request payload. +ISAKMP_CERTREQ : ISAKMP_GEN + TYPE cst 1 isakmp_certenc_cst + AUTHORITY raw +. + +# Hash payload. +ISAKMP_HASH : ISAKMP_GEN + DATA raw +. + +# Signature payload. +ISAKMP_SIG : ISAKMP_GEN + DATA raw +. + +# Nonce payload. +ISAKMP_NONCE : ISAKMP_GEN + DATA raw +. + +# Notify payload. +ISAKMP_NOTIFY : ISAKMP_GEN + DOI cst 4 isakmp_doi_cst,ipsec_doi_cst + PROTO cst 1 isakmp_proto_cst + SPI_SZ num 1 + MSG_TYPE cst 2 isakmp_notify_cst + SPI raw +. + +# Delete payload. +ISAKMP_DELETE : ISAKMP_GEN + DOI cst 4 isakmp_doi_cst,ipsec_doi_cst + PROTO cst 1 isakmp_proto_cst + SPI_SZ num 1 + NSPIS num 2 + SPI raw +. + +# Vendor ID payload. +ISAKMP_VENDOR : ISAKMP_GEN + ID raw +. diff --git a/sbin/isakmpd/isakmp_num.cst b/sbin/isakmpd/isakmp_num.cst new file mode 100644 index 00000000000..af292c8d8b2 --- /dev/null +++ b/sbin/isakmpd/isakmp_num.cst @@ -0,0 +1,156 @@ +# $Id: isakmp_num.cst,v 1.1 1998/11/15 00:03:48 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +# XXX Please fill in references to the drafts, chapter & verse for each +# constant group below. +# Also think about ranges, can they be specified diferently? Can we use +# these constants for vlidity checks? + +# ISAKMP payload type. +ISAKMP_PAYLOAD + NONE 0 + SA 1 + PROPOSAL 2 + TRANSFORM 3 + KEY_EXCH 4 + ID 5 + CERT 6 + CERT_REQ 7 + HASH 8 + SIG 9 + NONCE 10 + NOTIFY 11 + DELETE 12 + VENDOR 13 + RESERVED_MIN 14 + RESERVED_MAX 127 + PRIVATE_MIN 128 + PRIVATE_MAX 255 +. + +# ISAKMP exchange types. +ISAKMP_EXCH + NONE 0 + BASE 1 + ID_PROT 2 + AUTH_ONLY 3 + AGGRESSIVE 4 + INFO 5 + FUTURE_MIN 6 + FUTURE_MAX 31 + DOI_MIN 32 + DOI_MAX 255 +. + +# ISAKMP flags. +ISAKMP_FLAGS + ENC 1 + COMMIT 2 + AUTH_ONLY 4 +. + +# ISAKMP certificate encoding. +ISAKMP_CERTENC + NONE 0 + PKCS 1 + PGP 2 + DNS 3 + X509_SIG 4 + X509_KE 5 + KERBEROS 6 + CRL 7 + ARL 8 + SPKI 9 + X509_ATTR 10 + RESERVED_MIN 11 + RESERVED_MAX 255 +. + +# ISAKMP Notify message types. +ISAKMP_NOTIFY + INVALID_PAYLOAD_TYPE 1 + DOI_NOT_SUPPORTED 2 + SITUATION_NOT_SUPPORTED 3 + INVALID_COOKIE 4 + INVALID_MAJOR_VERSION 5 + INVALID_MINOR_VERSION 6 + INVALID_EXCHANGE_TYPE 7 + INVALID_FLAGS 8 + INVALID_MESSAGE_ID 9 + INVALID_PROTOCOL_ID 10 + INVALID_SPI 11 + INVALID_TRANSFORM_ID 12 + ATTRIBUTES_NOT_SUPPORTED 13 + NO_PROPOSAL_CHOSEN 14 + BAD_PROPOSAL_SYNTAX 15 + PAYLOAD_MALFORMED 16 + INVALID_KEY_INFORMATION 17 + INVALID_ID_INFORMATION 18 + INVALID_CERT_ENCODING 19 + INVALID_CERTIFICATE 20 + CERT_TYPE_UNSUPPORTED 21 + INVALID_CERT_AUTHORITY 22 + INVALID_HASH_INFORMATION 23 + AUTHENTICATION_FAILED 24 + INVALID_SIGNATURE 25 + ADDRESS_NOTIFICATION 26 + NOTIFY_SA_LIFETIME 27 + CERTIFICATE_UNAVAILABLE 28 + UNSUPPORTED_EXCHANGE_TYPE 29 + UNEQUAL_PAYLOAD_LENGTHS 30 + RESERVED_MIN 31 + RESERVED_MAX 8191 + PRIVATE_MIN 8192 + PRIVATE_MAX 16383 + STATUS_CONNECTED 16384 + STATUS_RESERVED1_MIN 16385 + STATUS_RESERVED1_MAX 24575 + STATUS_DOI_MIN 12576 + STATUS_DOI_MAX 32767 + STATUS_PRIVATE_MIN 32768 + STATUS_PRIVATE_MAX 40959 + STATUS_RESERVED2_MIN 40960 + STATUS_RESERVED2_MAX 65535 +. + +# ISAKMP DOI Identifier. +ISAKMP_DOI + ISAKMP 0 +. + +# ISAKMP Protocol ID. +ISAKMP_PROTO + ISAKMP 1 +. diff --git a/sbin/isakmpd/isakmpd.8 b/sbin/isakmpd/isakmpd.8 new file mode 100644 index 00000000000..41edaf860c8 --- /dev/null +++ b/sbin/isakmpd/isakmpd.8 @@ -0,0 +1,116 @@ +.\" $Id: isakmpd.8,v 1.1 1998/11/15 00:03:48 niklas Exp $ +.\" +.\" Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Ericsson Radio Systems. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" This code was written under funding by Ericsson Radio Systems. +.\" +.\" Manual page, using -mandoc macros +.\" +.Dd July 31, 1998 +.Dt ISAKMPD 8 +.Os +.Sh NAME +.Nm isakmpd +.Nd ISAKMP/Oakley aka IKE key management daemon +.Sh SYNOPSIS +.Nm isakmpd +.Op Fl c Ar config-file +.Op Fl d +.Op Fl D Ar debug-class=level +.Op Fl f Ar fifo +.Op Fl n +.Op Fl p Ar listen-port +.Op Fl P Ar local-port +.Op Fl r Ar seed +.Sh DESCRIPTION +The +.Nm ISAKMP +daemon establishes security associations for encrypted +and/or authenticated network traffic. +.Pp +The daemon listens to a named pipe +.Pa isakmpd.fifo +for user requests and on a +.Nm PF_ENCAP +socket for kernel requests. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c +If given, the +.Fl c +option specifies an alternate configuration file instead of +.Pa /etc/isakmpd.conf . +.It Fl d +The +.Fl d +option is used to make the daemon run in the foreground, logging to stderr. +.It Fl D +This argument is possible to specify many times. It takes a parameter of +the form: class=level where both class and level are numbers. Class denotes +a debugging class, and level the level you want that debugging class to +limit debug printouts at. I.e. all debug printouts above the level specified +will not output anything. +.It Fl f +The +.Fl f +option specifies the fifo (aka named pipe) where the daemon listens for +user requests. If the path given is a dash, +.Nm isakmpd +will listen to stdin instead. +.It Fl n +When the +.Fl n +option is given, the kernel will not take part in the negotiations. +This is a non-destructive mode so to say, in that it won't alter any +SAs in the IPSEC stack. +.It Fl r +If given a deterministic random number sequence will be used internally. +This is useful for setting up regression tests. +.It Fl p +The +.Fl p +option specifies the listen port the daemon will bind to. +.It Fl P +On the other hand, the port spcified to capital +.Fl P +will be what the daemon binds its local end to when acting as initiator. +.El +.Pp +.Sh SEE ALSO +.Xr isakmpd.conf 5 , +.Xr photurisd 8 , +.Xr ipsec 4 . +.Sh HISTORY +The ISAKMP/Oakley keymanagement protocol is described in the internet drafts +.Nm draft-ietf-ipsec-isakmp , +.Nm draft-ietf-ipsec-ipsec-doi & +.Nm draft-ietf-ipsec-isakmp-oakley . +This implementation was done 1998 by Niklas Hallqvist and Niels Provos, +sponsored by Ericsson Radio Systems. + diff --git a/sbin/isakmpd/isakmpd.c b/sbin/isakmpd/isakmpd.c new file mode 100644 index 00000000000..0b334d4405f --- /dev/null +++ b/sbin/isakmpd/isakmpd.c @@ -0,0 +1,220 @@ +/* $Id: isakmpd.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "app.h" +#include "conf.h" +#include "init.h" +#include "log.h" +#include "sysdep.h" +#include "timer.h" +#include "transport.h" +#include "udp.h" +#include "ui.h" + +extern char *optarg; +extern int optind; + +/* + * Set if -d is given, currently just for running in the foreground and log + * to stderr instead of syslog. + */ +int debug = 0; + +/* + * Use -r seed to initalize random numbers to a deterministic sequence. + */ +extern int regrand; + +/* + * If we receive a SIGHUP signal, this flag gets set to show we need to + * reconfigure ASAP. + */ +static int sighupped = 0; + +static void +usage () +{ + fprintf (stderr, + "usage: %s [-d] [-c config-file] [-D class=level] [-f fifo] [-n]\n" + " [-p listen-port] [-P local-port] [-r seed]\n", + sysdep_progname ()); + exit (1); +} + +static void +parse_args (int argc, char *argv[]) +{ + int ch, cls, level; + + while ((ch = getopt (argc, argv, "c:dD:f:np:P:r:")) != -1) { + switch (ch) { + case 'c': + conf_path = optarg; + break; + case 'd': + debug++; + break; + case 'D': + if (sscanf (optarg, "%d=%d", &cls, &level) != 2) + log_print ("parse_args: -D argument unparseable: %s", optarg); + else + log_debug_cmd (cls, level); + break; + case 'f': + ui_fifo = optarg; + break; + case 'n': + app_none++; + break; + case 'p': + udp_default_port = atoi (optarg); + break; + case 'P': + udp_bind_port = atoi (optarg); + break; + case 'r': + srandom (strtoul (optarg, NULL, 0)); + regrand = 1; + break; + case '?': + default: + usage (); + } + } + argc -= optind; + argv += optind; +} + +/* Reinitialize after a SIGHUP reception. */ +static void +reinit (void) +{ + /* Reread config file. */ + conf_init (); + + /* XXX Rescan interfaces. */ + + sighupped = 0; +} + +static void +sighup (int sig) +{ + sighupped = 1; +} + +int +main (int argc, char *argv[]) +{ + fd_set rfds, wfds; + int n, m; + struct timeval tv, *timeout = &tv; + + parse_args (argc, argv); + init (); + if (!debug) + { + if (daemon (0, 0)) + log_fatal ("daemon"); + /* Switch to syslog. */ + log_to (0); + } + + /* Reinitialize on HUP reception. */ + signal (SIGHUP, sighup); + + while (1) + { + /* If someone has sent SIGHUP to us, reconfigure. */ + if (sighupped) + reinit (); + + /* Setup the descriptors to look for incoming messages at. */ + FD_ZERO (&rfds); + n = transport_fd_set (&rfds); + FD_SET (ui_socket, &rfds); + if (ui_socket + 1 > n) + n = ui_socket + 1; + + /* + * XXX Some day we might want to deal with an abstract application + * class instead, with many instantiations possible. + */ + if (!app_none) + { + FD_SET (app_socket, &rfds); + if (app_socket + 1 > n) + n = app_socket + 1; + } + + /* Setup the descriptors that have pending messages to send. */ + FD_ZERO (&wfds); + m = transport_pending_wfd_set (&wfds); + if (m > n) + n = m; + + /* Find out when the next timed event is. */ + timer_next_event (&timeout); + + n = select (n, &rfds, &wfds, 0, timeout); + if (n == -1) + { + log_error ("select"); + /* + * In order to give the unexpected error condition time to resolve + * without letting this process eat up all available CPU we sleep + * for a short while. + */ + sleep (1); + } + else if (n) + { + transport_handle_messages (&rfds); + transport_send_messages (&wfds); + if (FD_ISSET (ui_socket, &rfds)) + ui_handler (); + if (!app_none && FD_ISSET (app_socket, &rfds)) + app_handler (); + } + timer_handle_expirations (); + } +} diff --git a/sbin/isakmpd/isakmpd.conf.5 b/sbin/isakmpd/isakmpd.conf.5 new file mode 100644 index 00000000000..1f767dafb70 --- /dev/null +++ b/sbin/isakmpd/isakmpd.conf.5 @@ -0,0 +1,175 @@ +.\" $Id: isakmpd.conf.5,v 1.1 1998/11/15 00:03:49 niklas Exp $ +.\" +.\" Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Ericsson Radio Systems. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" This code was written under funding by Ericsson Radio Systems. +.\" +.\" Manual page, using -mandoc macros +.\" +.Dd October 10, 1998 +.Dt ISAKMPD.CONF 8 +.Os +.Sh NAME +.Nm isakmpd.conf +.Nd Configuration file for isakmpd +.Sh DESCRIPTION +The +.Nm isakmpd.conf +is the configuration file for +.Nm isakmpd +daemon managing security association and key management for the +.Nm IPSEC +layer of the kernel's networking stack. +.Pp +The file is of a well known type of format called .INI style, named after +the suffix used by an overrated windowing environment for its configuration +files. This format consists of sections, each beginning with a line looking +like: +.Bd -literal +[Section name] +.Ed +Between the brackets is the name of the section following this section header. +Inside a section many tag/value pairs can be stored, each one looking like: +.Bd -literal +Tag=Value +.Ed +If the value needs more space than fits on a single line it's possible to +continue it on the next by ending the first with a backspace character +immediately before the newline character. This method can extend a value for +an arbitrary amount of lines. +.Pp +Comments can be put anywhere in the file by using a hash mark, "#". Then +the comment goes on to the end of the line. +.Pp +.Sh EXAMPLE +An example of a configuration file: +.Pp +.Bd -literal +# A configuration sample for the isakmpd ISAKMP/Oakley (aka IKE) daemon. + +[General] +Retransmits= 10 + +[Main mode initiator] +Offered-transforms= BLF-SHA-16,DES-MD5 + +[Main mode responder] +# XXX Not yet supported. +#Accepted-transforms= BLF-SHA-M1024,BLF-SHA-EC185,BLF-SHA-EC155,DES-MD5 + +[DES-MD5] +ENCRYPTION_ALGORITHM= DES_CBC +HASH_ALGORITHM= MD5 +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= MODP_768 + +[BLF-SHA-16] +ENCRYPTION_ALGORITHM= BLOWFISH_CBC +KEY_LENGTH= 128 +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= MODP_1024 + +[BLF-SHA-M1024] +ENCRYPTION_ALGORITHM= BLOWFISH +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= MODP_1024 + +[BLF-SHA-EC155] +ENCRYPTION_ALGORITHM= BLOWFISH +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= EC2N_155 + +[BLF-SHA-EC185] +ENCRYPTION_ALGORITHM= BLOWFISH +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= EC2N_185 + +[Quick mode initiator] +Offered-suites= QM-ESP-DES-MD5-SUITE,QM-AH-MD5-ESP-DES-SUITE + +[Quick mode responder] +# XXX Not yet supported. +#Accepted-suites= QM-ESP-DES-MD5-SUITE,QM-AH-MD5-ESP-DES-SUITE + +[QM-ESP-DES-MD5-SUITE] +Protocols= QM-ESP-DES-MD5 + +[QM-ESP-DES-MD5] +PROTOCOL_ID= IPSEC_ESP +Transforms= QM-ESP-DES-MD5-XF + +[QM-ESP-DES-MD5-XF] +TRANSFORM_ID= DES +ENCAPSULATION_MODE= TUNNEL +AUTHENTICATION_ALGORITHM= HMAC_MD5 +Life= LIFE_600_SECS,LIFE_32_MB + +[LIFE_600_SECS] +SA_LIFE_TYPE= SECONDS +SA_LIFE_DURATION= 600 + +[LIFE_32_MB] +SA_LIFE_TYPE= KILOBYTES +SA_LIFE_DURATION= 32768 + +[QM-AH-MD5-ESP-DES-SUITE] +Protocols= QM-AH-MD5,QM-ESP-DES + +[QM-AH-MD5] +PROTOCOL_ID= IPSEC_AH +Transforms= QM-AH-MD5-XF + +[QM-AH-MD5-XF] +TRANSFORM_ID= MD5 +ENCAPSULATION_MODE= TUNNEL + +[QM-ESP-DES] +PROTOCOL_ID= IPSEC_ESP +Transforms= QM-ESP-DES-XF + +[QM-ESP-DES-XF] +TRANSFORM_ID= DES +ENCAPSULATION_MODE= TUNNEL + +[PRE_SHARED] +# A general pre-shared key used for everyone. XXX Should be per-peer later. +KEY= mekmitasdigoat + +[RSA_SIG] +CERT= /etc/isakmpd_cert +PRIVKEY= /etc/isakmpd_key +PUBKEY= /etc/isakmpd_key.pub +.Ed +.Pp +.Sh SEE ALSO +.Xr isakmpd 8 . + diff --git a/sbin/isakmpd/isakmpd.conf.sample b/sbin/isakmpd/isakmpd.conf.sample new file mode 100644 index 00000000000..60eb1bf3a04 --- /dev/null +++ b/sbin/isakmpd/isakmpd.conf.sample @@ -0,0 +1,111 @@ +# $Id: isakmpd.conf.sample,v 1.1 1998/11/15 00:03:49 niklas Exp $ + +# A configuration sample for the isakmpd ISAKMP/Oakley (aka IKE) daemon. + +[General] +Retransmits= 10 + +[Main mode initiator] +Offered-transforms= BLF-SHA-16,DES-MD5 + +[Main mode responder] +# XXX Not yet supported. +#Accepted-transforms= BLF-SHA-M1024,BLF-SHA-EC185,BLF-SHA-EC155,DES-MD5 + +[DES-MD5] +ENCRYPTION_ALGORITHM= DES_CBC +HASH_ALGORITHM= MD5 +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= MODP_768 +Life= LIFE_600_SECS + +[BLF-SHA-16] +ENCRYPTION_ALGORITHM= BLOWFISH_CBC +KEY_LENGTH= 128 +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= MODP_1024 +Life= LIFE_600_SECS + +[BLF-SHA-M1024] +ENCRYPTION_ALGORITHM= BLOWFISH +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= MODP_1024 +Life= LIFE_600_SECS + +[BLF-SHA-EC155] +ENCRYPTION_ALGORITHM= BLOWFISH +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= EC2N_155 +Life= LIFE_600_SECS + +[BLF-SHA-EC185] +ENCRYPTION_ALGORITHM= BLOWFISH +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= PRE_SHARED +GROUP_DESCRIPTION= EC2N_185 +Life= LIFE_600_SECS + +[Quick mode initiator] +#Offered-suites= QM-ESP-DES-SUITE,\ +# QM-ESP-DES-MD5-SUITE,QM-AH-MD5-ESP-DES-SUITE +Offered-suites= QM-ESP-DES-SUITE + +[Quick mode responder] +# XXX Not yet supported. +#Accepted-suites= QM-ESP-DES-MD5-SUITE,QM-AH-MD5-ESP-DES-SUITE + +[QM-ESP-DES-SUITE] +Protocols= QM-ESP-DES + +[QM-ESP-DES-MD5-SUITE] +Protocols= QM-ESP-DES-MD5 + +[QM-ESP-DES-MD5] +PROTOCOL_ID= IPSEC_ESP +Transforms= QM-ESP-DES-MD5-XF + +[QM-ESP-DES-MD5-XF] +TRANSFORM_ID= DES +ENCAPSULATION_MODE= TUNNEL +AUTHENTICATION_ALGORITHM= HMAC_MD5 +Life= LIFE_600_SECS,LIFE_32_MB + +[LIFE_600_SECS] +SA_LIFE_TYPE= SECONDS +SA_LIFE_DURATION= 600 + +[LIFE_32_MB] +SA_LIFE_TYPE= KILOBYTES +SA_LIFE_DURATION= 32768 + +[QM-AH-MD5-ESP-DES-SUITE] +Protocols= QM-AH-MD5,QM-ESP-DES + +[QM-AH-MD5] +PROTOCOL_ID= IPSEC_AH +Transforms= QM-AH-MD5-XF + +[QM-AH-MD5-XF] +TRANSFORM_ID= MD5 +ENCAPSULATION_MODE= TUNNEL + +[QM-ESP-DES] +PROTOCOL_ID= IPSEC_ESP +Transforms= QM-ESP-DES-XF + +[QM-ESP-DES-XF] +TRANSFORM_ID= DES +ENCAPSULATION_MODE= TUNNEL +Life= LIFE_600_SECS,LIFE_32_MB + +[PRE_SHARED] +# A general pre-shared key used for everyone. XXX Should be per-peer later. +KEY= mekmitasdigoat + +[RSA_SIG] +CERT= /etc/isakmpd_cert +PRIVKEY= /etc/isakmpd_key +PUBKEY= /etc/isakmpd_key.pub diff --git a/sbin/isakmpd/isakmpd_cert.sample b/sbin/isakmpd/isakmpd_cert.sample Binary files differnew file mode 100644 index 00000000000..b573cb0152f --- /dev/null +++ b/sbin/isakmpd/isakmpd_cert.sample diff --git a/sbin/isakmpd/isakmpd_key.pub.sample b/sbin/isakmpd/isakmpd_key.pub.sample Binary files differnew file mode 100644 index 00000000000..edce091937c --- /dev/null +++ b/sbin/isakmpd/isakmpd_key.pub.sample diff --git a/sbin/isakmpd/isakmpd_key.sample b/sbin/isakmpd/isakmpd_key.sample Binary files differnew file mode 100644 index 00000000000..e022e420842 --- /dev/null +++ b/sbin/isakmpd/isakmpd_key.sample diff --git a/sbin/isakmpd/log.c b/sbin/isakmpd/log.c new file mode 100644 index 00000000000..587198b75ec --- /dev/null +++ b/sbin/isakmpd/log.c @@ -0,0 +1,240 @@ +/* $Id: log.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "log.h" + +/* + * We cannot do the log strings dynamically sizeable as out of memory is one + * of the situations we need to report about. + */ +#define LOG_SIZE 200 + +static void _log_print (int, int, const char *, va_list); + +static FILE *log_output = stderr; +static int log_level[LOG_ENDCLASS]; + +void +log_to (FILE *f) +{ + if (!log_output && f) + closelog (); + log_output = f; + if (!f) + openlog ("isakmpd", 0, LOG_DAEMON); +} + +static void +_log_print (int error, int level, const char *fmt, va_list ap) +{ + char buffer[LOG_SIZE]; + int len; + + len = vsnprintf (buffer, LOG_SIZE, fmt, ap); + if (len < LOG_SIZE - 1 && error) + snprintf (buffer + len, LOG_SIZE - len, ": %s", strerror (errno)); + if (log_output) + { + fputs (buffer, log_output); + fputc ('\n', log_output); + } + else + syslog (level, buffer); +} + +void +#ifdef __STDC__ +log_debug (int cls, int level, const char *fmt, ...) +#else +log_debug (cls, level, clfmt, va_alist) + int cls; + int level; + const char *fmt; + va_dcl +#endif +{ + va_list ap; + + /* + * If we are not debugging this class, or the level is too low, just return. + */ + if (log_level[cls] == 0 || level > log_level[cls]) + return; +#ifdef __STDC__ + va_start (ap, fmt); +#else + va_start (ap); + fmt = va_arg (ap, const char *); +#endif + _log_print (0, LOG_DEBUG, fmt, ap); + va_end (ap); +} + +void +log_debug_buf (int cls, int level, const char *header, const u_int8_t *buf, + size_t sz) +{ + char s[73]; + int i, j; + + /* + * If we are not debugging this class, or the level is too low, just return. + */ + if (log_level[cls] == 0 || level > log_level[cls]) + return; + + log_debug (cls, level, "%s:", header); + for (i = j = 0; i < sz;) + { + sprintf (s + j, "%02x", buf[i++]); + j += 2; + if (i % 4 == 0) + { + if (i % 32 == 0) + { + s[j] = '\0'; + log_debug (cls, level, "%s", s); + j = 0; + } + else + s[j++] = ' '; + } + } + if (j) + { + s[j] = '\0'; + log_debug (cls, level, "%s", s); + } +} + +void +#ifdef __STDC__ +log_print (const char *fmt, ...) +#else +log_print (fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start (ap, fmt); +#else + va_start (ap); + fmt = va_arg (ap, const char *); +#endif + _log_print (0, LOG_NOTICE, fmt, ap); + va_end (ap); +} + +void +#ifdef __STDC__ +log_error (const char *fmt, ...) +#else +log_error (fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start (ap, fmt); +#else + va_start (ap); + fmt = va_arg (ap, const char *); +#endif + _log_print (1, LOG_ERR, fmt, ap); + va_end (ap); +} + +void +#ifdef __STDC__ +log_fatal (const char *fmt, ...) +#else +log_fatal (fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start (ap, fmt); +#else + va_start (ap); + fmt = va_arg (ap, const char *); +#endif + _log_print (1, LOG_CRIT, fmt, ap); + va_end (ap); + exit (1); +} + +void +log_debug_cmd (int cls, int level) +{ + if (cls < 0 || cls >= LOG_ENDCLASS) + { + log_print ("log_debug_cmd: invalid debugging class %d", cls); + return; + } + + if (level < 0) + { + log_print ("log_debug_cmd: invalid debugging level %d for class %d", + level, cls); + return; + } + + if (level == log_level[cls]) + log_print ("log_debug_cmd: log level unchanged for class %d", cls); + else + { + log_print ("log_debug_cmd: log level changed from %d to %d for class %d", + log_level[cls], level, cls); + log_level[cls] = level; + } +} diff --git a/sbin/isakmpd/log.h b/sbin/isakmpd/log.h new file mode 100644 index 00000000000..14679c41ccf --- /dev/null +++ b/sbin/isakmpd/log.h @@ -0,0 +1,55 @@ +/* $Id: log.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include <sys/types.h> +#include <stdio.h> + +enum log_classes { + LOG_MISC, LOG_TRANSPORT, LOG_MESSAGE, LOG_CRYPTO, LOG_TIMER, LOG_PF_ENCAP, + LOG_ENDCLASS +}; + +extern void log_debug (int, int, const char *, ...); +extern void log_debug_buf (int, int, const char *, const u_int8_t *, size_t); +extern void log_debug_cmd (int, int); +extern void log_error (const char *, ...); +extern void log_fatal (const char *, ...); +extern void log_print (const char *, ...); +extern void log_to (FILE *); + +#endif /* _LOG_H_ */ diff --git a/sbin/isakmpd/math_2n.c b/sbin/isakmpd/math_2n.c new file mode 100644 index 00000000000..fa5df464d49 --- /dev/null +++ b/sbin/isakmpd/math_2n.c @@ -0,0 +1,1045 @@ +/* $Id: math_2n.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * B2N is a module for doing arithmetic on the Field GF(2**n) which is + * isomorph to ring of polynomials GF(2)[x]/p(x) where p(x) is an + * irreduciable polynomial over GF(2)[x] with grade n. + * + * First we need functions which operate on GF(2)[x], operation + * on GF(2)[x]/p(x) can be done as for Z_p then. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "util.h" +#include "math_2n.h" + +u_int8_t hex2int (char); + +static char int2hex[] = "0123456789abcdef"; +CHUNK_TYPE b2n_mask[CHUNK_BITS] = { + 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, +#if CHUNK_BITS > 8 + 0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000, +#if CHUNK_BITS > 16 + 0x00010000,0x00020000,0x00040000,0x00080000, + 0x00100000,0x00200000,0x00400000,0x00800000, + 0x01000000,0x02000000,0x04000000,0x08000000, + 0x10000000,0x20000000,0x40000000,0x80000000, +#endif +#endif +}; + +/* Misc */ +u_int8_t +hex2int (char c) +{ + if (c <= '9') + return c - '0'; + if (c <= 'f') + return 10 + c - 'a'; + + return 0; +} + + +void +b2n_random (b2n_ptr n, u_int32_t bits) +{ + b2n_resize (n, (CHUNK_MASK + bits) >> CHUNK_SHIFTS); + + getrandom ((u_int8_t *)n->limp, CHUNK_BYTES * n->chunks); + + /* Get the number of significant bits right */ + if (bits & CHUNK_MASK) + { + CHUNK_TYPE m = (((1 << ((bits & CHUNK_MASK)-1)) - 1) << 1) | 1; + n->limp[n->chunks-1] &= m; + } + + n->dirty = 1; +} + +/* b2n management functions */ + +void +b2n_init (b2n_ptr n) +{ + n->chunks = 0; + n->limp = NULL; +} + +void +b2n_clear (b2n_ptr n) +{ + /* XXX Does all systems deal with free (NULL) nicely? */ + free (n->limp); +} + +void +b2n_resize (b2n_ptr n, unsigned int chunks) +{ + int old = n->chunks; + int size; + CHUNK_TYPE *new; + + if (chunks == 0) + chunks = 1; + + if (chunks == old) + return; + + size = CHUNK_BYTES * chunks; + + /* XXX - is there anything I can do here? */ + new = realloc (n->limp, size); + if (new == NULL) + return ; + + n->limp = new; + n->chunks = chunks; + n->bits = chunks << CHUNK_SHIFTS; + n->dirty = 1; + + if (chunks > old) + memset (n->limp + old, 0, size - CHUNK_BYTES*old); +} + +/* Simple assignment functions */ + +void +b2n_set (b2n_ptr d, b2n_ptr s) +{ + if (d == s) + return; + + b2n_sigbit (s); + b2n_resize (d, (CHUNK_MASK + s->bits) >> CHUNK_SHIFTS); + memcpy (d->limp, s->limp, CHUNK_BYTES*d->chunks); + d->bits = s->bits; + d->dirty = s->dirty; +} + +void +b2n_set_null (b2n_ptr n) +{ + b2n_resize (n , 1); + n->limp[0] = n->bits = n->dirty = 0; +} + +void +b2n_set_ui (b2n_ptr n, unsigned int val) +{ +#if CHUNK_BITS < 32 + int i, chunks; + + chunks = (CHUNK_BYTES - 1 + sizeof (val))/CHUNK_BYTES; + + b2n_resize (n, chunks ); + + for (i = 0; i < chunks; i++) + { + n->limp[i] = val & CHUNK_BMASK; + val >>= CHUNK_BITS; + } +#else + b2n_resize (n, 1); + n->limp[0] = val; +#endif + n->dirty = 1; +} + +/* Only takes hex at the moment */ + +void +b2n_set_str (b2n_ptr n, char *str) +{ + int i, j, w, len, chunks; + CHUNK_TYPE tmp; + + if (strncasecmp (str, "0x", 2)) + return; + + /* Make the hex string even lengthed */ + len = strlen (str) - 2; + if (len & 1) + { + len ++; + str ++; + } + else + str += 2; + + len /= 2; + + chunks = (CHUNK_BYTES - 1 + len)/CHUNK_BYTES; + b2n_resize (n, chunks); + memset (n->limp, 0, CHUNK_BYTES * n->chunks); + + for (w = 0, i = 0; i < chunks; i++) + { + tmp = 0; + for (j = (i == 0 ? ((len-1) % CHUNK_BYTES)+1 : CHUNK_BYTES); j > 0; j--) + { + tmp <<= 8; + tmp |= (hex2int(str[w]) << 4) | hex2int(str[w+1]); + w += 2; + } + n->limp[chunks-1-i] = tmp; + } + + n->dirty = 1; +} + +/* Output function, mainly for debugging perpurses */ + +void +b2n_print (b2n_ptr n) +{ + int i, j, w, flag = 0; + int left; + char buffer[2*CHUNK_BYTES]; + CHUNK_TYPE tmp; + + left = ((((7 + b2n_sigbit (n)) >> 3) - 1) % CHUNK_BYTES) + 1; + printf("0x"); + for (i = 0; i < n->chunks; i++) + { + tmp = n->limp[n->chunks-1-i]; + memset (buffer, '0', sizeof (buffer)); + for (w = 0, j = (i == 0 ? left : CHUNK_BYTES); j > 0; j--) + { + buffer[w++] = int2hex[(tmp >> 4) & 0xF]; + buffer[w++] = int2hex[tmp & 0xF]; + tmp >>= 8; + } + + for (j = (i == 0 ? left - 1: CHUNK_BYTES - 1); j >= 0; j--) + if (flag || (i == n->chunks - 1 && j == 0) || + buffer[2*j] != '0' || buffer[2*j+1] != '0') + { + putchar (buffer[2*j]); + putchar (buffer[2*j+1]); + flag = 1; + } + } + printf("\n"); +} + +int +b2n_sprint (char *buf, b2n_ptr n) +{ + int i, k, j, w, flag = 0; + int left; + char buffer[2*CHUNK_BYTES]; + CHUNK_TYPE tmp; + + left = ((((7 + b2n_sigbit (n)) >> 3) - 1) % CHUNK_BYTES) + 1; + + strcpy (buf, "0x"); k = 2; + for (i = 0; i < n->chunks; i++) + { + tmp = n->limp[n->chunks-1-i]; + memset (buffer, '0', sizeof (buffer)); + for (w = 0, j = (i == 0 ? left : CHUNK_BYTES); j > 0; j--) + { + buffer[w++] = int2hex[(tmp >> 4) & 0xF]; + buffer[w++] = int2hex[tmp & 0xF]; + tmp >>= 8; + } + + for (j = (i == 0 ? left - 1: CHUNK_BYTES - 1); j >= 0; j--) + if (flag || (i == n->chunks - 1 && j == 0) || + buffer[2*j] != '0' || buffer[2*j+1] != '0') + { + buf[k++] = buffer[2*j]; + buf[k++] = buffer[2*j+1]; + flag = 1; + } + } + + buf [k++] = 0; + return k; +} + +/* Arithmetic functions */ + +u_int32_t +b2n_sigbit (b2n_ptr n) +{ + int i, j; + + if (!n->dirty) + return n->bits; + + for (i = n->chunks-1; i > 0; i--) + if (n->limp[i]) + break; + + if (!n->limp[i]) + return 0; + + for (j = CHUNK_MASK; j > 0; j--) + if (n->limp[i] & b2n_mask[j]) + break; + + n->bits = (i << CHUNK_SHIFTS) + j + 1; + n->dirty = 0; + return n->bits; +} + + +/* + * Addition on GF(2)[x] is nice, its just an XOR. + */ + +void +b2n_add (b2n_ptr d, b2n_ptr a, b2n_ptr b) +{ + int i; + b2n_ptr bmin, bmax; + + if (!b2n_cmp_null (a)) + { + b2n_set (d, b); + return; + } + + if (!b2n_cmp_null (b)) + { + b2n_set (d, a); + return; + } + + bmin = B2N_MIN (a,b); + bmax = B2N_MAX (a,b); + + b2n_resize (d, bmax->chunks); + + for (i = 0; i < bmin->chunks; i++) + d->limp[i] = bmax->limp[i] ^ bmin->limp[i]; + + /* + * If d is not bmax, we have to copy the rest of the bytes, and also + * need to adjust to number of relevant bits. + */ + if (d != bmax) + { + for ( ; i < bmax->chunks; i++) + d->limp[i] = bmax->limp[i]; + + d->bits = bmax->bits; + } + + /* + * Help to converse memory. When the result of the addition is zero + * truncate the used amount of memory. + */ + if (d != bmax && !b2n_cmp_null (d)) + b2n_set_null (d); + else + d->dirty = 1; +} + + +/* + * Compare two polynomials. + */ + +int +b2n_cmp (b2n_ptr n, b2n_ptr m) +{ + int sn, sm; + int i; + + sn = b2n_sigbit (n); + sm = b2n_sigbit (m); + + if (sn > sm) + return 1; + if (sn < sm) + return -1; + + for (i = n->chunks-1; i >= 0; i--) + if (n->limp[i] > m->limp[i]) + return 1; + else if (n->limp[i] < m->limp[i]) + return -1; + + return 0; +} + +int +b2n_cmp_null (b2n_ptr a) +{ + int i = 0; + + do + { + if (a->limp[i]) + return 1; + } while (++i < a->chunks); + + return 0; +} + +/* + * Left shift, needed for polynomial multiplication. + */ + +void +b2n_lshift (b2n_ptr d, b2n_ptr n, unsigned int s) +{ + int i, maj, min, chunks; + u_int16_t bits = b2n_sigbit (n), add; + CHUNK_TYPE *p, *op; + + if (!s) + { + b2n_set (d, n); + return; + } + + maj = s >> CHUNK_SHIFTS; + min = s & CHUNK_MASK; + + add = (!(bits&CHUNK_MASK) || ((bits&CHUNK_MASK) + min) > CHUNK_MASK) ? 1 : 0; + chunks = n->chunks; + b2n_resize (d, chunks + maj + add); + memmove (d->limp + maj, n->limp, CHUNK_BYTES * chunks); + + if (maj) + memset (d->limp, 0, CHUNK_BYTES * maj); + if (add) + d->limp[d->chunks-1] = 0; + + /* If !min there are no bit shifts, we are done */ + if (!min) + return; + + op = p = &d->limp[d->chunks-1]; + for (i = d->chunks-2; i >= maj; i--) + { + op--; + *p-- = (*p << min) | (*op >> (CHUNK_BITS - min)); + } + *p <<= min; + + d->dirty = 0; + d->bits = bits + (maj << CHUNK_SHIFTS) + min; +} + +/* + * Right shift, needed for polynomial division. + */ + +void +b2n_rshift (b2n_ptr d, b2n_ptr n, unsigned int s) +{ + int maj, min, size = n->chunks, newsize; + b2n_ptr tmp; + + if (!s) + { + b2n_set (d, n); + return; + } + + maj = s >> CHUNK_SHIFTS; + + newsize = size - maj; + + if (size < maj) + { + b2n_set_null (d); + return; + } + + min = (CHUNK_BITS - (s & CHUNK_MASK)) & CHUNK_MASK; + if (min) + { + if ((b2n_sigbit (n) & CHUNK_MASK) > min) + newsize++; + + b2n_lshift (d, n, min); + tmp = d; + } + else + tmp = n; + + memmove (d->limp, tmp->limp+maj+(min ? 1 : 0), CHUNK_BYTES * newsize); + b2n_resize (d, newsize); + + d->bits = tmp->bits - ((maj + (min ? 1 : 0)) << CHUNK_SHIFTS); +} + +/* + * Normal polynomial multiplication. + */ + +void +b2n_mul (b2n_ptr d, b2n_ptr n, b2n_ptr m) +{ + int i, j; + b2n_t tmp, tmp2; + + if (!b2n_cmp_null (m) || !b2n_cmp_null (n)) + { + b2n_set_null (d); + return; + } + + if (b2n_sigbit (m) == 1) + { + b2n_set (d, n); + return; + } + + if (b2n_sigbit (n) == 1) + { + b2n_set (d, m); + return; + } + + b2n_init (tmp); + b2n_init (tmp2); + + b2n_set (tmp, B2N_MAX (n, m)); + b2n_set (tmp2, B2N_MIN (n, m)); + + b2n_set_null (d); + + for (i = 0; i < tmp2->chunks; i++) + if (tmp2->limp[i]) + for (j = 0; j < CHUNK_BITS; j++) + { + if (tmp2->limp[i] & b2n_mask[j]) + b2n_add (d, d, tmp); + + b2n_lshift (tmp, tmp, 1); + } + else + b2n_lshift (tmp, tmp, CHUNK_BITS); + + b2n_clear (tmp); + b2n_clear (tmp2); +} + +/* + * Squaring in this polynomial ring is more efficient than normal + * multiplication. + */ + +void +b2n_square (b2n_ptr d, b2n_ptr n) +{ + int i, j, maj, min, bits, chunk; + b2n_t t; + + maj = b2n_sigbit (n); + min = maj & CHUNK_MASK; + maj = (maj + CHUNK_MASK) >> CHUNK_SHIFTS; + + b2n_init (t); + b2n_resize (t, 2*maj + ((CHUNK_MASK + 2*min) >> CHUNK_SHIFTS)); + + chunk = 0; + bits = 0; + + for (i = 0; i < maj; i++) + if (n->limp[i]) + for (j = 0; j < CHUNK_BITS; j++) + { + if (n->limp[i] & b2n_mask[j]) + t->limp[chunk] ^= b2n_mask[bits]; + + bits += 2; + if (bits >= CHUNK_BITS) + { + chunk++; + bits &= CHUNK_MASK; + } + } + else + chunk += 2; + + t->dirty = 1; + B2N_SWAP (d, t); + b2n_clear (t); +} + +/* + * Normal polynomial division. + * These functions are far from optimal in speed. + */ + +void +b2n_div_q (b2n_ptr d, b2n_ptr n, b2n_ptr m) +{ + b2n_t r; + + b2n_init (r); + b2n_div (d, r, n, m); + b2n_clear (r); +} + +void +b2n_div_r (b2n_ptr r, b2n_ptr n, b2n_ptr m) +{ + b2n_t q; + + b2n_init (q); + b2n_div (q, r, n, m); + b2n_clear (q); +} + +void +b2n_div (b2n_ptr q, b2n_ptr r, b2n_ptr n, b2n_ptr m) +{ + int sn, sm, i, j, len, bits; + b2n_t nenn, div, shift, mask; + + /* If Teiler > Zaehler, the result is 0 */ + if ((sm = b2n_sigbit (m)) > (sn = b2n_sigbit (n))) + { + b2n_set_null (q); + b2n_set (r, n); + return; + } + + if (sm == 0) + /* Division by Zero */ + return; + else if (sm == 1) + { + /* Division by the One-Element */ + b2n_set (q, n); + b2n_set_null (r); + return; + } + + b2n_init (nenn); + b2n_init (div); + b2n_init (shift); + b2n_init (mask); + + b2n_set (nenn, n); + b2n_set (div, m); + b2n_set (shift, m); + b2n_set_ui (mask, 1); + + b2n_resize (q, (sn - sm + CHUNK_MASK) >> CHUNK_SHIFTS); + memset (q->limp, 0, CHUNK_BYTES * q->chunks); + + b2n_lshift (shift, shift, sn - sm); + b2n_lshift (mask, mask, sn - sm); + + /* Number of significant octets */ + len = (sn - 1) >> CHUNK_SHIFTS; + /* The first iteration is done over the relevant bits */ + bits = (CHUNK_MASK + sn) & CHUNK_MASK; + for (i = len; i >= 0 && b2n_sigbit (nenn) >= sm; i--) + for (j = (i == len ? bits : CHUNK_MASK); j >= 0 && b2n_sigbit (nenn) >= sm; j--) + { + if (nenn->limp[i] & b2n_mask[j]) + { + b2n_sub (nenn, nenn, shift); + b2n_add (q, q, mask); + } + b2n_rshift (shift, shift, 1); + b2n_rshift (mask, mask, 1); + } + + + B2N_SWAP (r, nenn); + + b2n_clear (nenn); + b2n_clear (div); + b2n_clear (shift); +} + + +/* + * Functions for Operation on GF(2**n) ~= GF(2)[x]/p(x). + */ + +void +b2n_mod (b2n_ptr m, b2n_ptr n, b2n_ptr p) +{ + int bits, size; + b2n_div_r (m, n, p); + + bits = b2n_sigbit (m); + size = ((CHUNK_MASK + bits) >> CHUNK_SHIFTS); + if (size == 0) + size = 1; + if (m->chunks > size) + b2n_resize (m, size); + + m->bits = bits; + m->dirty = 0; +} + +void +b2n_gcd (b2n_ptr e, b2n_ptr go, b2n_ptr ho) +{ + b2n_t g, h; + + b2n_init (g); b2n_set (g, go); + b2n_init (h); b2n_set (h, ho); + + while (b2n_cmp_null (h)) + { + b2n_mod (g, g, h); + B2N_SWAP (g,h); + } + + B2N_SWAP (e, g); + + b2n_clear (g); + b2n_clear (h); +} + +void +b2n_mul_inv (b2n_ptr ga, b2n_ptr be, b2n_ptr p) +{ + b2n_t a; + + b2n_init (a); + b2n_set_ui (a,1); + + b2n_div_mod (ga, a, be, p); + + b2n_clear (a); +} + +void +b2n_div_mod (b2n_ptr ga, b2n_ptr a, b2n_ptr be, b2n_ptr p) +{ + b2n_t s0, s1, s2, q, r0, r1; + + /* There is no multiplicative inverse to Null */ + if (!b2n_cmp_null(be)) + { + b2n_set_null (ga); + return; + } + + b2n_init (s0); b2n_init (s1); b2n_init (s2); + b2n_init (r0); b2n_init (r1); + b2n_init (q); + + b2n_set (r0, p); + b2n_set (r1, be); + + b2n_set_null (s0); + b2n_set (s1, a); + + while (b2n_cmp_null (r1)) + { + b2n_div(q, r0, r0, r1); + B2N_SWAP (r0, r1); + + b2n_mul (s2, q, s1); + b2n_mod (s2, s2, p); + b2n_sub (s2, s0, s2); + + B2N_SWAP (s0, s1); + B2N_SWAP (s1, s2); + } + B2N_SWAP (ga, s0); + + b2n_clear (s0); b2n_clear (s1); b2n_clear (s2); + b2n_clear (r0); b2n_clear (r1); + b2n_clear (q); +} + +/* + * The trace tells us if there do exist any square roots + * for 'a' in GF(2)[x]/p(x). The number of square roots is + * 2 - 2*Trace. + * If z is a square root, z + 1 is the other. + */ + +void +b2n_trace (b2n_ptr ho, b2n_ptr a, b2n_ptr p) +{ + int i, m = b2n_sigbit (p) - 1; + b2n_t h; + + b2n_init (h); + b2n_set (h, a); + + for (i = 0; i < m - 1; i++) + { + b2n_square (h, h); + b2n_mod (h, h, p); + + b2n_add (h, h, a); + } + B2N_SWAP (ho, h); + + b2n_clear (h); +} + +/* + * The halftrace yields the square root if the degree of the + * irreduceable polynomial is odd. + */ + +void +b2n_halftrace (b2n_ptr ho, b2n_ptr a, b2n_ptr p) +{ + int i, m = b2n_sigbit (p) - 1; + b2n_t h; + + b2n_init (h); + b2n_set (h, a); + + for (i = 0; i < (m - 1)/2; i++) + { + b2n_square (h, h); + b2n_mod (h, h, p); + b2n_square (h, h); + b2n_mod (h, h, p); + + b2n_add (h, h, a); + } + + B2N_SWAP (ho, h); + + b2n_clear (h); +} + +/* + * Solving the equation: y**2 + y = b in GF(2**m) where ip is the + * irreduceable polynomial. If m is odd, use the half trace. + */ + +void +b2n_sqrt (b2n_ptr zo, b2n_ptr b, b2n_ptr ip) +{ + int i, m = b2n_sigbit (ip) - 1; + b2n_t w, p, temp, z; + + if (!b2n_cmp_null (b)) + { + b2n_set_null (z); + return; + } + + if (m & 1) + { + b2n_halftrace (zo, b, ip); + return; + } + + b2n_init (z); + b2n_init (w); + b2n_init (p); + b2n_init (temp); + do { + b2n_random (p, m); + b2n_set_null (z); + b2n_set (w, p); + for (i = 1; i < m; i++) + { + b2n_square (z, z); /* z**2 */ + b2n_mod (z, z, ip); + + b2n_square (w, w); /* w**2 */ + b2n_mod (w, w, ip); + + b2n_mul (temp, w, b); /* w**2 * b */ + b2n_mod (temp, temp, ip); + b2n_add (z, z, temp); /* z**2 + w**2 + b */ + + b2n_add (w, w, p); /* w**2 + p */ + } + } while (!b2n_cmp_null (w)); + + B2N_SWAP (zo, z); + + b2n_clear (w); + b2n_clear (p); + b2n_clear (temp); + b2n_clear (z); +} + + +/* + * Exponentiation modulo a polynomial. + */ + +void +b2n_exp_mod (b2n_ptr d, b2n_ptr b0, u_int32_t e, b2n_ptr p) +{ + b2n_t u, b; + + b2n_init (u); + b2n_set_ui (u, 1); + b2n_init (b); + b2n_mod (b, b0, p); + + while (e) + { + if (e & 1) + { + b2n_mul (u, u, b); + b2n_mod (u, u, p); + } + b2n_square (b, b); + b2n_mod (b, b, p); + e >>= 1; + } + + B2N_SWAP (d, u); + b2n_clear (u); + b2n_clear (b); +} + +/* + * Low-level function to speed up scalar multiplication with + * elliptic curves. + * Multiplies a normal number by 3. + */ + +/* + * Normal addition behaves as Z_{2**n} and not F_{2**n}. + */ + +void +b2n_nadd (b2n_ptr d0, b2n_ptr a0, b2n_ptr b0) +{ + int i, carry; + + b2n_ptr a, b; + b2n_t d; + + if (!b2n_cmp_null (a0)) + { + b2n_set (d0, b0); + return; + } + + if (!b2n_cmp_null (b0)) + { + b2n_set (d0, a0); + return; + } + + b2n_init (d); + a = B2N_MAX (a0, b0); + b = B2N_MIN (a0, b0); + + b2n_resize (d, a->chunks + 1); + + for (carry = i = 0; i < b->chunks; i++) + { + d->limp[i] = a->limp[i] + b->limp[i] + carry; + carry = (d->limp[i] < a->limp[i] ? 1 : 0); + } + + for ( ; i < a->chunks && carry; i++) + { + d->limp[i] = a->limp[i] + carry; + carry = (d->limp[i] < a->limp[i] ? 1 : 0); + } + + if (i < a->chunks) + memcpy (d->limp + i, a->limp + i, CHUNK_BYTES*(a->chunks - i)); + + d->dirty = 1; + B2N_SWAP (d0, d); + + b2n_clear (d); +} + +/* + * Very special sub, a > b. + */ + +void +b2n_nsub (b2n_ptr d0, b2n_ptr a, b2n_ptr b) +{ + int i, carry; + b2n_t d; + + if (b2n_cmp (a, b) <= 0) + { + b2n_set_null (d0); + return; + } + + b2n_init (d); + b2n_resize (d, a->chunks); + + for (carry = i = 0; i < b->chunks; i++) + { + d->limp[i] = a->limp[i] - b->limp[i] - carry; + carry = (d->limp[i] > a->limp[i] ? 1 : 0); + } + + for ( ; i < a->chunks && carry; i++) + { + d->limp[i] = a->limp[i] - carry; + carry = (d->limp[i] > a->limp[i] ? 1 : 0); + } + + if (i < a->chunks) + memcpy (d->limp + i, a->limp + i, CHUNK_BYTES*(a->chunks - i)); + + d->dirty = 1; + + B2N_SWAP (d0, d); + + b2n_clear (d); +} + +void +b2n_3mul (b2n_ptr d0, b2n_ptr e) +{ + b2n_t d; + + b2n_init (d); + b2n_lshift (d, e, 1); + + b2n_nadd (d0, d, e); + + b2n_clear (d); +} diff --git a/sbin/isakmpd/math_2n.h b/sbin/isakmpd/math_2n.h new file mode 100644 index 00000000000..0d5365a2670 --- /dev/null +++ b/sbin/isakmpd/math_2n.h @@ -0,0 +1,133 @@ +/* $Id: math_2n.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _MATH_2N_H +#define _MATH_2N_H_ + +/* + * The chunk size we use is variable, this allows speed ups + * for processors like the Alpha with 64bit words. + * XXX - b2n_mask is only up to 32 bit at the moment. + */ + +#define USE_32BIT /* XXX - This obviously needs fixing */ + +#ifdef USE_32BIT +#define CHUNK_TYPE u_int32_t +#define CHUNK_BITS 32 +#define CHUNK_SHIFTS 5 +#define CHUNK_BMASK 0xffffffff +#define CHUNK_MASK (CHUNK_BITS - 1) +#define CHUNK_BYTES (CHUNK_BITS >> 3) +#define CHUNK_NIBBLES (CHUNK_BITS >> 2) +#else +#define CHUNK_TYPE u_int8_t +#define CHUNK_BITS 8 +#define CHUNK_SHIFTS 3 +#define CHUNK_BMASK 0xff +#define CHUNK_MASK (CHUNK_BITS - 1) +#define CHUNK_BYTES (CHUNK_BITS >> 3) +#define CHUNK_NIBBLES (CHUNK_BITS >> 2) +#endif + +extern CHUNK_TYPE b2n_mask[CHUNK_BITS]; + +/* An element of GF(2**n), n = bits */ + +typedef struct { + u_int16_t chunks; + u_int16_t bits; + u_int8_t dirty; /* Sig bits are dirty */ + CHUNK_TYPE *limp; +} _b2n; + +typedef _b2n *b2n_ptr; +typedef _b2n b2n_t[1]; + +#define B2N_SET(x,y) (x)->chunks = (y)->chunks; (x)->bits = (y)->bits; \ + (x)->limp = (y)->limp; (x)->dirty = (y)->dirty; + +#define B2N_MIN(x,y) ((x)->chunks > (y)->chunks ? (y) : (x)) +#define B2N_MAX(x,y) ((x)->chunks > (y)->chunks ? (x) : (y)) + +void b2n_init (b2n_ptr); +void b2n_clear (b2n_ptr); +void b2n_resize (b2n_ptr, unsigned int); + +void b2n_random (b2n_ptr, u_int32_t); + +void b2n_set (b2n_ptr, b2n_ptr); +void b2n_set_null (b2n_ptr); +void b2n_set_ui (b2n_ptr, unsigned int); +void b2n_set_str (b2n_ptr, char *); + +void b2n_print (b2n_ptr); +int b2n_sprint (char *, b2n_ptr); + +int b2n_cmp (b2n_ptr, b2n_ptr); +int b2n_cmp_null (b2n_ptr); + +void b2n_add (b2n_ptr, b2n_ptr, b2n_ptr); +#define b2n_sub b2n_add +void b2n_lshift (b2n_ptr, b2n_ptr, unsigned int); +void b2n_rshift (b2n_ptr, b2n_ptr, unsigned int); +u_int32_t b2n_sigbit (b2n_ptr); + +void b2n_nadd (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_nsub (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_3mul (b2n_ptr, b2n_ptr); + +void b2n_mul (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_square (b2n_ptr, b2n_ptr); + +void b2n_div (b2n_ptr, b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_div_q (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_div_r (b2n_ptr, b2n_ptr, b2n_ptr); + +/* Operation on GF(2**n) */ +#define B2N_SWAP(x,y) {b2n_t _t_; B2N_SET(_t_, (x)); \ + B2N_SET ((x), (y)); B2N_SET ((y),_t_); } + +void b2n_mod (b2n_ptr, b2n_ptr, b2n_ptr); + +void b2n_gcd (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_mul_inv (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_div_mod (b2n_ptr, b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_trace (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_halftrace (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_sqrt (b2n_ptr, b2n_ptr, b2n_ptr); +void b2n_exp_mod (b2n_ptr, b2n_ptr, u_int32_t, b2n_ptr); + +#endif /* _MATH_2N_H_ */ diff --git a/sbin/isakmpd/math_ec2n.c b/sbin/isakmpd/math_ec2n.c new file mode 100644 index 00000000000..f8232e21b65 --- /dev/null +++ b/sbin/isakmpd/math_ec2n.c @@ -0,0 +1,314 @@ +/* $Id: math_ec2n.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <stdio.h> + +#include "math_2n.h" +#include "math_ec2n.h" + +void +ec2np_init (ec2np_ptr n) +{ + b2n_init (n->x); + b2n_init (n->y); + n->inf = 0; +} + +void +ec2np_clear (ec2np_ptr n) +{ + b2n_clear (n->x); + b2n_clear (n->y); +} + +void +ec2np_set (ec2np_ptr d, ec2np_ptr n) +{ + if (d == n) + return; + + d->inf = n->inf; + b2n_set (d->x, n->x); + b2n_set (d->y, n->y); +} + +/* Group */ + +void +ec2ng_init (ec2ng_ptr n) +{ + b2n_init (n->a); + b2n_init (n->b); + b2n_init (n->p); +} + +void +ec2ng_clear (ec2ng_ptr n) +{ + b2n_clear (n->a); + b2n_clear (n->b); + b2n_clear (n->p); +} + +void +ec2ng_set (ec2ng_ptr d, ec2ng_ptr n) +{ + b2n_set (d->a, n->a); + b2n_set (d->b, n->b); + b2n_set (d->p, n->p); +} + +/* Arithmetic functions */ + +void +ec2np_right (b2n_ptr n, ec2np_ptr p, ec2ng_ptr g) +{ + b2n_t temp; + + b2n_init (temp); + /* First calc x**3 + ax**2 + b */ + b2n_square (n, p->x); + b2n_mod (n, n, g->p); + + b2n_mul (temp, g->a, n); /* a*x**2 */ + b2n_mod (temp, temp, g->p); + + b2n_mul (n, n, p->x); /* x**3 */ + b2n_mod (n, n, g->p); + + b2n_add (n, n, temp); + b2n_add (n, n, g->b); + + b2n_clear (temp); +} + +int +ec2np_ison (ec2np_ptr p, ec2ng_ptr g) +{ + int res; + + b2n_t x, y, temp; + + if (p->inf) + return 1; + + b2n_init (x); + b2n_init (y); + b2n_init (temp); + + /* First calc x**3 + ax**2 + b */ + ec2np_right (x, p, g); + + /* Now calc y**2 + xy */ + b2n_square (y, p->y); + b2n_mod (y, y, g->p); + + b2n_mul (temp, p->y, p->x); + b2n_mod (temp, temp, g->p); + + b2n_add (y, y, temp); + + res = !b2n_cmp (x, y); + + b2n_clear (x); + b2n_clear (y); + b2n_clear (temp); + + return res; +} + +int +ec2np_find_y (ec2np_ptr p, ec2ng_ptr g) +{ + b2n_t right; + + b2n_init (right); + ec2np_right (right, p, g); /* Right sight of equation */ + b2n_mul_inv (p->y, p->x, g->p); + + b2n_square (p->y, p->y); + b2n_mod (p->y, p->y, g->p); + + b2n_mul (right, right, p->y); /* x^-2 * right */ + b2n_mod (right, right, g->p); + + b2n_sqrt (p->y, right, g->p); /* Find root */ + b2n_mul (p->y, p->y, p->x); + b2n_mod (p->y, p->y, g->p); + + return 1; +} + +void +ec2np_add (ec2np_ptr d, ec2np_ptr a, ec2np_ptr b, ec2ng_ptr g) +{ + b2n_t lambda, temp; + ec2np_t pn; + + /* Check for Neutral Element */ + if (b->inf) + { + ec2np_set (d, a); + return; + } + if (a->inf) + { + ec2np_set (d, b); + return; + } + + if (!b2n_cmp (a->x, b->x) && (b2n_cmp (a->y, b->y) || !b2n_cmp_null (a->x))) + { + d->inf = 1; + b2n_set_null (d->x); + b2n_set_null (d->y); + return; + } + + b2n_init (lambda); + b2n_init (temp); + ec2np_init (pn); + + if (b2n_cmp (a->x, b->x)) + { + b2n_add (temp, a->x, b->x); + b2n_add (lambda, a->y, b->y); + b2n_div_mod (lambda, lambda, temp, g->p); + + b2n_square (pn->x, lambda); + b2n_mod (pn->x, pn->x, g->p); + + b2n_add (pn->x, pn->x, lambda); + b2n_add (pn->x, pn->x, g->a); + b2n_add (pn->x, pn->x, a->x); + b2n_add (pn->x, pn->x, b->x); + } + else + { + b2n_div_mod (lambda, b->y, b->x, g->p); + b2n_add (lambda, lambda, b->x); + + b2n_square (pn->x, lambda); + b2n_mod (pn->x, pn->x, g->p); + b2n_add (pn->x, pn->x, lambda); + b2n_add (pn->x, pn->x, g->a); + } + + b2n_add (pn->y, b->x, pn->x); + + b2n_mul (pn->y, pn->y, lambda); + b2n_mod (pn->y, pn->y, g->p); + + b2n_add (pn->y, pn->y, pn->x); + b2n_add (pn->y, pn->y, b->y); + + EC2NP_SWAP (d, pn); + + ec2np_clear (pn); + b2n_clear (lambda); + b2n_clear (temp); +} + +void +ec2np_mul (ec2np_ptr d, ec2np_ptr a, b2n_ptr e, ec2ng_ptr g) +{ + int i, j, bits, start; + b2n_t h, k; + ec2np_t q, mina; + + if (!b2n_cmp_null (e)) + { + d->inf = 1; + b2n_set_null (d->x); + b2n_set_null (d->y); + return; + } + + b2n_init (h); + b2n_init (k); + + ec2np_init (q); + ec2np_init (mina); + ec2np_set (q, a); + + /* Create the point -a */ + ec2np_set (mina, a); + b2n_add (mina->y, mina->y, mina->x); + + b2n_set (k, e); + b2n_3mul (h, k); + b2n_resize (k, h->chunks); + + /* + * This is low level but can not be avoided, since we have to do single + * bit checks on h and k. + */ + bits = b2n_sigbit (h); + if ((bits & CHUNK_MASK) == 1) + { + start = ((CHUNK_MASK + bits) >> CHUNK_SHIFTS) - 2; + bits = CHUNK_BITS; + } + else + { + start = ((CHUNK_MASK + bits) >> CHUNK_SHIFTS) - 1; + bits = ((bits - 1) & CHUNK_MASK); + } + + /* + * This is the addition, subtraction method which is faster because + * we avoid one out of three additions (mean). + */ + + for (i = start; i >= 0; i--) + for (j = (i == start ? bits : CHUNK_BITS) - 1; j >= 0; j--) + if (i > 0 || j > 0) + { + ec2np_add (q, q, q, g); + if ((h->limp[i] & b2n_mask[j]) && !(k->limp[i] & b2n_mask[j])) + ec2np_add (q, q, a, g); + else if (!(h->limp[i] & b2n_mask[j]) && (k->limp[i] & b2n_mask[j])) + ec2np_add (q, q, mina, g); + } + + EC2NP_SWAP (d, q); + + b2n_clear (k); + b2n_clear (h); + + ec2np_clear (q); + ec2np_clear (mina); +} diff --git a/sbin/isakmpd/math_ec2n.h b/sbin/isakmpd/math_ec2n.h new file mode 100644 index 00000000000..22d5d9e5422 --- /dev/null +++ b/sbin/isakmpd/math_ec2n.h @@ -0,0 +1,89 @@ +/* $Id: math_ec2n.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _MATH_EC2N_H +#define _MATH_EC2N_H_ + +/* Definitions for points on an elliptic curve */ + +typedef struct { + int inf; /* Are we the point at infinity ? */ + b2n_t x, y; +} _ec2n_point; + +typedef _ec2n_point *ec2np_ptr; +typedef _ec2n_point ec2np_t[1]; + +#define EC2NP_SWAP(k,n) {int _i_; _i_ = (k)->inf; (k)->inf = (n)->inf; \ + (n)->inf = _i_; B2N_SWAP ((k)->x, (n)->x); B2N_SWAP ((k)->y, (n)->y);} + +void ec2np_init (ec2np_ptr); +void ec2np_clear (ec2np_ptr); +void ec2np_set (ec2np_ptr, ec2np_ptr); + +#define ec2np_set_x_ui(n, y) b2n_set_ui ((n)->x, y) +#define ec2np_set_y_ui(n, x) b2n_set_ui ((n)->y, x) +#define ec2np_set_x_str(n, y) b2n_set_str ((n)->x, y) +#define ec2np_set_y_str(n, x) b2n_set_str ((n)->y, x) + +/* Definitions for the group to which the points to belong to */ + +typedef struct { + b2n_t a, b, p; +} _ec2n_group; + +typedef _ec2n_group *ec2ng_ptr; +typedef _ec2n_group ec2ng_t[1]; + +void ec2ng_init (ec2ng_ptr); +void ec2ng_clear (ec2ng_ptr); +void ec2ng_set (ec2ng_ptr, ec2ng_ptr); + +#define ec2ng_set_a_ui(n, x) b2n_set_ui ((n)->a, x) +#define ec2ng_set_b_ui(n, x) b2n_set_ui ((n)->b, x) +#define ec2ng_set_p_ui(n, x) b2n_set_ui ((n)->p, x) +#define ec2ng_set_a_str(n, x) b2n_set_str ((n)->a, x) +#define ec2ng_set_b_str(n, x) b2n_set_str ((n)->b, x) +#define ec2ng_set_p_str(n, x) b2n_set_str ((n)->p, x) + +/* Functions for computing on the elliptic group */ + +void ec2np_right (b2n_ptr n, ec2np_ptr, ec2ng_ptr); +int ec2np_ison (ec2np_ptr, ec2ng_ptr); +int ec2np_find_y (ec2np_ptr, ec2ng_ptr); +void ec2np_add (ec2np_ptr, ec2np_ptr, ec2np_ptr, ec2ng_ptr); +void ec2np_mul (ec2np_ptr, ec2np_ptr, b2n_ptr, ec2ng_ptr); + +#endif /* _MATH_2N_H_ */ diff --git a/sbin/isakmpd/math_group.c b/sbin/isakmpd/math_group.c new file mode 100644 index 00000000000..048ec3977ad --- /dev/null +++ b/sbin/isakmpd/math_group.c @@ -0,0 +1,533 @@ +/* $Id: math_group.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <gmp.h> +#include <stdlib.h> +#include <string.h> + +#include "gmp_util.h" +#include "math_2n.h" +#include "math_ec2n.h" +#include "math_group.h" + +#include "log.h" +#include "sysdep.h" + +/* We do not want to export these definitions */ +int modp_getlen (struct group *); +void modp_getraw (struct group *, mpz_ptr, u_int8_t *); +void modp_setraw (struct group *, mpz_ptr, u_int8_t *, int); +void modp_setrandom (struct group *, mpz_ptr); +void modp_operation (struct group *, mpz_ptr, mpz_ptr, mpz_ptr); + +int ec2n_getlen (struct group *); +void ec2n_getraw (struct group *, ec2np_ptr, u_int8_t *); +void ec2n_setraw (struct group *, ec2np_ptr, u_int8_t *, int); +void ec2n_setrandom (struct group *, ec2np_ptr); +void ec2n_operation (struct group *, ec2np_ptr, ec2np_ptr, ec2np_ptr); + +struct ec2n_group { + ec2np_t gen; /* Generator */ + ec2ng_t grp; + ec2np_t a, b, c, d; +}; + +struct modp_group { + mpz_t gen; /* Generator */ + mpz_t p; /* Prime */ + mpz_t a, b, c, d; +}; + +/* + * This module provides access to the operations on the specified group + * and is absolutly free of any cryptographic devices. This is math :-). + */ + +#define OAKLEY_GRP_1 1 +#define OAKLEY_GRP_2 2 +#define OAKLEY_GRP_3 3 +#define OAKLEY_GRP_4 4 + +/* Describe preconfigured MODP groups */ + +/* + * The Generalized Number Field Sieve has an asymptotic running time + * of: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))), where q is the + * group order, e.g. q = 2**768. + */ + +struct modp_dscr oakley_modp[] = +{ + { OAKLEY_GRP_1, 72, /* This group is insecure, only sufficient for DES */ + "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", + "0x02" + }, + { OAKLEY_GRP_2, 82, /* This group is a bit better */ + "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" + "FFFFFFFFFFFFFFFF", + "0x02" + } +}; + +/* Describe preconfigured EC2N groups */ + +/* + * Related collision-search methods can compute discrete logarithmns + * in O(sqrt(r)), r being the subgroup order. + */ + +struct ec2n_dscr oakley_ec2n[] = { + { OAKLEY_GRP_3, 76, /* This group is also considered insecure (P1363) */ + "0x0800000000000000000000004000000000000001", + "0x7b", + "0x00", + "0x7338f" }, + { OAKLEY_GRP_4, 91, + "0x020000000000000000000000000000200000000000000001", + "0x18", + "0x00", + "0x1ee9" }, +}; + +struct group groups[] = { + { + MODP, OAKLEY_GRP_1, 0, &oakley_modp[0], NULL, NULL, NULL, NULL, NULL, + (int (*) (struct group *))modp_getlen, + (void (*) (struct group *, void *, u_int8_t *))modp_getraw, + (void (*) (struct group *, void *, u_int8_t *, int))modp_setraw, + (void (*) (struct group *, void *))modp_setrandom, + (void (*) (struct group *, void *, void *, void *))modp_operation + }, + { + MODP, OAKLEY_GRP_2, 0, &oakley_modp[1], NULL, NULL, NULL, NULL, NULL, + (int (*) (struct group *))modp_getlen, + (void (*) (struct group *, void *, u_int8_t *))modp_getraw, + (void (*) (struct group *, void *, u_int8_t *, int))modp_setraw, + (void (*) (struct group *, void *))modp_setrandom, + (void (*) (struct group *, void *, void *, void *))modp_operation + }, + { + EC2N, OAKLEY_GRP_3, 0, &oakley_ec2n[0], NULL, NULL, NULL, NULL, NULL, + (int (*) (struct group *))ec2n_getlen, + (void (*) (struct group *, void *, u_int8_t *))ec2n_getraw, + (void (*) (struct group *, void *, u_int8_t *, int))ec2n_setraw, + (void (*) (struct group *, void *))ec2n_setrandom, + (void (*) (struct group *, void *, void *, void *))ec2n_operation + }, + { + EC2N, OAKLEY_GRP_4, 0, &oakley_ec2n[1], NULL, NULL, NULL, NULL, NULL, + (int (*) (struct group *))ec2n_getlen, + (void (*) (struct group *, void *, u_int8_t *))ec2n_getraw, + (void (*) (struct group *, void *, u_int8_t *, int))ec2n_setraw, + (void (*) (struct group *, void *))ec2n_setrandom, + (void (*) (struct group *, void *, void *, void *))ec2n_operation + } +}; + + +/* + * Initalize the group structure for later use, + * this is done by converting the values given in the describtion + * and converting them to their native representation. + */ + +void +group_init (void) +{ + int i; + + for (i = sizeof (groups)/sizeof (groups[0]) -1; i >= 0; i--) + switch (groups[i].type) + { + case EC2N: /* Initalize an Elliptic Curve over GF(2**n) */ + ec2n_init (&groups[i]); + break; + + case MODP: /* Initalize an over GF(p) */ + modp_init (&groups[i]); + break; + + default: + log_print ("Unknown group type %d at index %d in group_init().", + groups[i].type, i); + break; + } +} + +struct group * +group_get (int id) +{ + struct group *new, *clone; + if (id < 1 || id > (sizeof (groups)/sizeof (groups[0]))) + return NULL; + + clone = &groups[id-1]; + + if ((new = malloc (sizeof (struct group))) == NULL) + { + log_print ("group_get: Out of memory"); + return (NULL); + } + + switch (clone->type) + { + case EC2N: + new = ec2n_clone (new, clone); + break; + case MODP: + new = modp_clone (new, clone); + break; + default: + log_print ("group_get: Unknown group type %d", clone->type); + free (new); + return (NULL); + } + return (new); +} + +void +group_free (struct group *grp) +{ + switch (grp->type) + { + case EC2N: + ec2n_free (grp); + break; + case MODP: + modp_free (grp); + default: + log_print ("group_free: Unknown group type %d", grp->type); + break; + } + free (grp); +} + +struct group * +modp_clone (struct group *new, struct group *clone) +{ + struct modp_group *new_grp, *clone_grp = clone->group; + + if ((new_grp = malloc (sizeof (struct modp_group))) == NULL) + { + log_print ("modp_clone: Out of memory"); + free (new); + return (NULL); + } + + memcpy (new, clone, sizeof (struct group)); + + new->group = new_grp; + mpz_init_set (new_grp->p, clone_grp->p); + + mpz_init_set (new_grp->gen, clone_grp->gen); + + mpz_init (new_grp->a); + mpz_init (new_grp->b); + mpz_init (new_grp->c); + + new->gen = new_grp->gen; + new->a = new_grp->a; + new->b = new_grp->b; + new->c = new_grp->c; + + return (new); +} + +void +modp_free (struct group *old) +{ + struct modp_group *grp = old->group; + + mpz_clear (grp->p); + mpz_clear (grp->gen); + mpz_clear (grp->a); + mpz_clear (grp->b); + mpz_clear (grp->c); + + free (grp); +} + +void +modp_init (struct group *group) +{ + struct modp_dscr *dscr = (struct modp_dscr *)group->group; + + struct modp_group *grp; + + if ((grp = malloc (sizeof (struct modp_group))) == NULL) + log_fatal ("modp_init: Out of memory"); + + group->bits = dscr->bits; + + mpz_init_set_str (grp->p, dscr->prime, 0); + + mpz_init_set_str (grp->gen, dscr->gen, 0); + + mpz_init (grp->a); + mpz_init (grp->b); + mpz_init (grp->c); + + group->gen = grp->gen; + group->a = grp->a; + group->b = grp->b; + group->c = grp->c; + + group->group = grp; +} + +struct group * +ec2n_clone (struct group *new, struct group *clone) +{ + struct ec2n_group *new_grp, *clone_grp = clone->group; + + if ((new_grp = malloc (sizeof (struct ec2n_group))) == NULL) + { + log_print ("ec2n_clone: Out of memory"); + free (new); + return (NULL); + } + + memcpy (new, clone, sizeof (struct group)); + + new->group = new_grp; + ec2ng_init (new_grp->grp); + ec2ng_set (new_grp->grp, clone_grp->grp); + + ec2np_init (new_grp->gen); + ec2np_set (new_grp->gen, clone_grp->gen); + + ec2np_init (new_grp->a); + ec2np_init (new_grp->b); + ec2np_init (new_grp->c); + + new->gen = new_grp->gen; + new->a = new_grp->a; + new->b = new_grp->b; + new->c = new_grp->c; + new->d = ((ec2np_ptr)new->a)->x; + + return (new); +} + +void +ec2n_free (struct group *old) +{ + struct ec2n_group *grp = old->group; + + ec2ng_clear (grp->grp); + ec2np_clear (grp->gen); + ec2np_clear (grp->a); + ec2np_clear (grp->b); + ec2np_clear (grp->c); + + free (grp); +} + +void +ec2n_init (struct group *group) +{ + struct ec2n_dscr *dscr = (struct ec2n_dscr *)group->group; + + struct ec2n_group *grp; + + if ((grp = malloc (sizeof (struct ec2n_group))) == NULL) + log_fatal ("ec2n_init: Out of memory"); + + group->bits = dscr->bits; + + ec2ng_init (grp->grp); + ec2ng_set_p_str (grp->grp, dscr->polynomial); + grp->grp->p->bits = b2n_sigbit (grp->grp->p); + ec2ng_set_a_str (grp->grp, dscr->a); + ec2ng_set_b_str (grp->grp, dscr->b); + + ec2np_init (grp->gen); ec2np_set_x_str (grp->gen, dscr->gen_x); + ec2np_find_y (grp->gen, grp->grp); + + /* Sanity check */ + if (!ec2np_ison (grp->gen, grp->grp)) + log_fatal ("ec2n_init: Generator is not on curve"); + + ec2np_init (grp->a); + ec2np_init (grp->b); + ec2np_init (grp->c); + + group->gen = grp->gen; + group->a = grp->a; + group->b = grp->b; + group->c = grp->c; + group->d = ((ec2np_ptr)group->a)->x; + + group->group = grp; +} + +int +modp_getlen (struct group *group) +{ + struct modp_group *grp = (struct modp_group *)group->group; + + return mpz_sizeinoctets (grp->p); +} + +void +modp_getraw (struct group *grp, mpz_ptr v, u_int8_t *d) +{ + mpz_getraw (d, v, grp->getlen (grp)); +} + +void +modp_setraw (struct group *grp, mpz_ptr d, u_int8_t *s, int l) +{ + mpz_setraw (d, s, l); +} + +void +modp_setrandom (struct group *grp, mpz_ptr d) +{ + int i, l = grp->getlen (grp); + u_int32_t tmp = 0; + + mpz_set_ui (d, 0); + + for (i = 0; i < l; i++) + { + if (i % 4) + tmp = sysdep_random(); + + mpz_mul_2exp (d, d, 8); + mpz_add_ui (d, d, tmp & 0xFF); + tmp >>= 8; + } +} + +void +modp_operation (struct group *group, mpz_ptr d, mpz_ptr a, mpz_ptr e) +{ + struct modp_group *grp = (struct modp_group *)group->group; + mpz_powm (d, a, e, grp->p); +} + +int +ec2n_getlen (struct group *group) +{ + struct ec2n_group *grp = (struct ec2n_group *)group->group; + int bits = b2n_sigbit (grp->grp->p) - 1; + + return (7 + bits) >> 3; +} + +void +ec2n_getraw (struct group *group, ec2np_ptr xo, u_int8_t *e) +{ + struct ec2n_group *grp = (struct ec2n_group *)group->group; + int chunks, bytes, i, j; + b2n_ptr x = xo->x; + CHUNK_TYPE tmp; + + bytes = b2n_sigbit (grp->grp->p) - 1; + chunks = (CHUNK_MASK + bytes) >> CHUNK_SHIFTS; + bytes = ((7 + (bytes & CHUNK_MASK)) >> 3); + + for (i = chunks-1; i >= 0; i--) + { + tmp = (i >= x->chunks ? 0 : x->limp[i]); + for (j = (i == chunks - 1 ? bytes : CHUNK_BYTES) - 1; j >= 0; j--) + { + e[j] = tmp & 0xFF; + tmp >>= 8; + } + e += (i == chunks - 1 ? bytes : CHUNK_BYTES); + } +} + +void +ec2n_setraw (struct group *grp, ec2np_ptr out, u_int8_t *s, int l) +{ + int len, bytes, i, j; + b2n_ptr outx = out->x; + CHUNK_TYPE tmp; + + len = (CHUNK_BYTES - 1 + l)/CHUNK_BYTES; + b2n_resize (outx, len); + + bytes = ((l - 1) % CHUNK_BYTES) + 1; + + for (i = len - 1; i >= 0; i--) + { + tmp = 0; + for (j = (i == len - 1 ? bytes : CHUNK_BYTES); j > 0; j--) + { + tmp <<= 8; + tmp |= *s++; + } + outx->limp[i] = tmp; + } +} + +void +ec2n_setrandom (struct group *group, ec2np_ptr x) +{ + b2n_ptr d = x->x; + struct ec2n_group *grp = (struct ec2n_group *)group->group; + b2n_random (d, b2n_sigbit (grp->grp->p) - 1); +} + +/* + * This is an attempt at operation abstraction. It can happen + * that we need to initalize the y variable for the operation + * to proceed correctly. When this is the case operation has + * to supply the variable 'a' with the chunks of the Y cooridnate + * set to zero. + */ + +void +ec2n_operation (struct group *grp, ec2np_ptr d, ec2np_ptr a, ec2np_ptr e) +{ + b2n_ptr ex = e->x; + struct ec2n_group *group = (struct ec2n_group *)grp->group; + + if (a->y->chunks == 0) + ec2np_find_y (a, group->grp); + + ec2np_mul (d, a, ex, group->grp); +} diff --git a/sbin/isakmpd/math_group.h b/sbin/isakmpd/math_group.h new file mode 100644 index 00000000000..1005456db97 --- /dev/null +++ b/sbin/isakmpd/math_group.h @@ -0,0 +1,96 @@ +/* $Id: math_group.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _MATH_GROUP_H +#define _MATH_GROUP_H_ + +enum groups { + MODP, /* F_p, Z modulo a prime */ + EC2N, /* Elliptic Curve over the Field GF(2**N) */ + ECP, /* Elliptic Curve over the Field Z_p */ +}; + +/* + * The group on which diffie hellmann calculations are done. + */ + +struct group { + enum groups type; + int id; /* Group ID */ + int bits; /* Number of key bits provided by this group */ + void *group; + void *a, *b, *c, *d; + void *gen; /* Group Generator */ + int (*getlen) (struct group *); + void (*getraw) (struct group *, void *, u_int8_t *); + void (*setraw) (struct group *, void *, u_int8_t *, int); + void (*setrandom) (struct group *, void *); + void (*operation) (struct group *, void *, void *, void *); +}; + +/* Description of an Elliptic Group over GF(2**n) for Boot-Strapping */ + +struct ec2n_dscr { + int id; + int bits; /* Key Bits provided by this group */ + char *polynomial; /* Irreduceable polynomial */ + char *gen_x; /* X - Coord. of Generator */ + char *a, *b; /* Curve Parameters */ +}; + +/* Description of F_p for Boot-Strapping */ + +struct modp_dscr { + int id; + int bits; /* Key Bits provided by this group */ + char *prime; /* Prime */ + char *gen; /* Generator */ +}; + +/* Prototypes */ + +void group_init (void); +void group_free (struct group *); +struct group *group_get (int); + +void ec2n_free (struct group *); +struct group *ec2n_clone (struct group *, struct group *); +void ec2n_init (struct group *); + +void modp_free (struct group *); +struct group *modp_clone (struct group *, struct group *); +void modp_init (struct group *); + +#endif /* _MATH_GROUP_H_ */ diff --git a/sbin/isakmpd/message.c b/sbin/isakmpd/message.c new file mode 100644 index 00000000000..8e809cf4834 --- /dev/null +++ b/sbin/isakmpd/message.c @@ -0,0 +1,1765 @@ +/* $Id: message.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <machine/endian.h> +#include <stdlib.h> +#include <string.h> + +#include "attribute.h" +#include "cert.h" +#include "constants.h" +#include "crypto.h" +#include "doi.h" +#include "exchange.h" +#include "field.h" +#include "isakmp.h" +#include "log.h" +#include "message.h" +#include "sa.h" +#include "timer.h" +#include "transport.h" +#include "util.h" + +#ifdef __GNUC__ +#define INLINE __inline +#else +#define INLINE +#endif + +/* A local set datatype, coincidentally fd_set suits our purpose fine. */ +typedef fd_set set; +#define ISSET FD_ISSET +#define SET FD_SET +#define ZERO FD_ZERO + +static int message_check_duplicate (struct message *); +static void message_dump_raw (char *, struct message *); +static int message_encrypt (struct message *); +static int message_index_payload (struct message *, struct payload *, u_int8_t, + u_int8_t *); +static int message_parse_transform (struct message *, struct payload *, + u_int8_t, u_int8_t *); +static int message_validate_cert (struct message *, struct payload *); +static int message_validate_cert_req (struct message *, struct payload *); +static int message_validate_delete (struct message *, struct payload *); +static int message_validate_hash (struct message *, struct payload *); +static int message_validate_id (struct message *, struct payload *); +static int message_validate_key_exch (struct message *, struct payload *); +static int message_validate_nonce (struct message *, struct payload *); +static int message_validate_notify (struct message *, struct payload *); +static int message_validate_proposal (struct message *, struct payload *); +static int message_validate_sa (struct message *, struct payload *); +static int message_validate_sig (struct message *, struct payload *); +static int message_validate_transform (struct message *, struct payload *); +static int message_validate_vendor (struct message *, struct payload *); + +static int (*message_validate_payload[]) (struct message *, struct payload *) = +{ + message_validate_sa, message_validate_proposal, message_validate_transform, + message_validate_key_exch, message_validate_id, message_validate_cert, + message_validate_cert_req, message_validate_hash, message_validate_sig, + message_validate_nonce, message_validate_notify, message_validate_delete, + message_validate_vendor +}; + +static struct field *fields[] = { + isakmp_sa_fld, isakmp_prop_fld, isakmp_transform_fld, isakmp_ke_fld, + isakmp_id_fld, isakmp_cert_fld, isakmp_certreq_fld, isakmp_hash_fld, + isakmp_sig_fld, isakmp_nonce_fld, isakmp_notify_fld, isakmp_delete_fld, + isakmp_vendor_fld +}; + +/* + * Fields used for checking monotonic increasing of proposal and transform + * numbers. + */ +static u_int8_t *last_sa = 0; +static int last_prop_no; +static u_int8_t *last_prop = 0; +static int last_xf_no; + +/* + * Allocate a message structure bound to transport T, and with a first + * segment buffer sized SZ, copied from BUF if given. + */ +struct message * +message_alloc (struct transport *t, u_int8_t *buf, size_t sz) +{ + struct message *msg; + int i; + + /* + * We use calloc(3) because it zeroes the structure which we rely on in + * message_free when determining what sub-allocations to free. + */ + msg = (struct message *)calloc (1, sizeof *msg); + if (!msg) + return 0; + msg->iov = calloc (1, sizeof *msg->iov); + if (!msg->iov) + { + message_free (msg); + return 0; + } + msg->iov[0].iov_len = sz; + msg->iov[0].iov_base = malloc (sz); + if (!msg->iov[0].iov_base) + { + message_free (msg); + return 0; + } + msg->iovlen = 1; + if (buf) + memcpy (msg->iov[0].iov_base, buf, sz); + msg->nextp = msg->iov[0].iov_base + ISAKMP_HDR_NEXT_PAYLOAD_OFF; + msg->transport = t; + for (i = ISAKMP_PAYLOAD_SA; i < ISAKMP_PAYLOAD_RESERVED_MIN; i++) + TAILQ_INIT (&msg->payload[i]); + TAILQ_INIT (&msg->post_send); + log_debug (LOG_MESSAGE, 90, "message_alloc: allocated %p", msg); + return msg; +} + +/* + * Allocate a message sutiable for a reply to MSG. Just allocate an empty + * ISAKMP header as the first segment. + */ +struct message * +message_alloc_reply (struct message *msg) +{ + struct message *reply; + + reply = message_alloc (msg->transport, 0, ISAKMP_HDR_SZ); + reply->exchange = msg->exchange; + reply->isakmp_sa = msg->isakmp_sa; + return reply; +} + +/* Free up all resources used by the MSG message. */ +void +message_free (struct message *msg) +{ + int i; + struct payload *payload, *next; + struct post_send *node; + + log_debug (LOG_MESSAGE, 20, "message_free: freeing %p", msg); + if (!msg) + return; + if (msg->orig && msg->orig != msg->iov[0].iov_base) + free (msg->orig); + if (msg->iov) + { + for (i = 0; i < msg->iovlen; i++) + if (msg->iov[i].iov_base) + free (msg->iov[i].iov_base); + free (msg->iov); + } + if (msg->retrans) + timer_remove_event (msg->retrans); + for (i = ISAKMP_PAYLOAD_SA; i < ISAKMP_PAYLOAD_RESERVED_MIN; i++) + for (payload = TAILQ_FIRST (&msg->payload[i]); payload; payload = next) + { + next = TAILQ_NEXT (payload, link); + free (payload); + } + while ((node = TAILQ_FIRST (&msg->post_send)) != 0) + TAILQ_REMOVE (&msg->post_send, TAILQ_FIRST (&msg->post_send), link); + free (msg); +} + +/* + * Generic ISAKMP parser. + * MSG is the ISAKMP message to be parsed. NEXT is the type of the first + * payload to be parsed, and it's pointed to by BUF. ACCEPTED_PAYLOADS + * tells what payloads are accepted and FUNC is a pointer to a function + * to be called for each payload found. Returns the total length of the + * parsed payloads. + */ +static int +message_parse_payloads (struct message *msg, struct payload *p, u_int8_t next, + u_int8_t *buf, set *accepted_payloads, + int (*func) (struct message *, struct payload *, + u_int8_t, u_int8_t *)) +{ + u_int8_t payload; + u_int16_t len; + int sz = 0; + + do + { + log_debug (LOG_MESSAGE, 50, + "message_parse_payloads: offset 0x%x payload %s", + buf - (u_int8_t *)msg->iov[0].iov_base, + constant_name (isakmp_payload_cst, next)); + + /* Does this payload's header fit? */ + if (buf + ISAKMP_GEN_SZ + > (u_int8_t *)msg->iov[0].iov_base + msg->iov[0].iov_len) + { + log_print ("message_parse_payloads: short message"); + message_drop (msg, ISAKMP_NOTIFY_UNEQUAL_PAYLOAD_LENGTHS, 0, 0, 1); + return -1; + } + + /* Ponder on the payload that is at BUF... */ + payload = next; + + /* Look at the next payload's type. */ + next = GET_ISAKMP_GEN_NEXT_PAYLOAD (buf); + if (next >= ISAKMP_PAYLOAD_RESERVED_MIN) + { + log_print ("message_parse_payloads: invalid next payload type %d " + "in payload of type %d", next, payload); + message_drop (msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE, 0, 0, 1); + return -1; + } + + /* Reserved fields in ISAKMP messages should be zero. */ + if (GET_ISAKMP_GEN_RESERVED (buf) != 0) + { + log_print ("message_parse_payloads: reserved field non-zero"); + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 1); + return -1; + } + + /* + * Decode the payload length field. + */ + len = GET_ISAKMP_GEN_LENGTH (buf); + + /* + * Check if the current payload is one of the accepted ones at this + * stage. + */ + if (!ISSET (payload, accepted_payloads)) + { + log_print ("message_parse_payloads: payload type %d unexpected", + payload); + message_drop (msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE, 0, 0, 1); + return -1; + } + + /* Call the payload handler specified by the caller. */ + if (func (msg, p, payload, buf)) + return -1; + + /* Advance to next payload. */ + buf += len; + sz += len; + } + while (next != ISAKMP_PAYLOAD_NONE); + return sz; +} + +/* + * Parse a proposal payload found in message MSG. PAYLOAD is always + * ISAKMP_PAYLOAD_PROPOSAL and ignored in here. It's needed as the API for + * message_parse_payloads requires it. BUF points to the proposal's + * generic payload header. + */ +static int +message_parse_proposal (struct message *msg, struct payload *p, + u_int8_t payload, u_int8_t *buf) +{ + set payload_set; + + /* Put the proposal into the proposal bucket. */ + message_index_payload (msg, p, payload, buf); + + ZERO (&payload_set); + SET (ISAKMP_PAYLOAD_TRANSFORM, &payload_set); + if (message_parse_payloads (msg, + TAILQ_LAST (&msg->payload + [ISAKMP_PAYLOAD_PROPOSAL], + payload_head), + ISAKMP_PAYLOAD_TRANSFORM, + buf + ISAKMP_PROP_SPI_OFF + + GET_ISAKMP_PROP_SPI_SZ (buf), + &payload_set, message_parse_transform) == -1) + return -1; + + return 0; +} + +static int +message_parse_transform (struct message *msg, struct payload *p, + u_int8_t payload, u_int8_t *buf) +{ + /* Put the transform into the transform bucket. */ + message_index_payload (msg, p, payload, buf); + + log_debug (LOG_MESSAGE, 50, "Transform %d's attributes", + GET_ISAKMP_TRANSFORM_NO (buf)); + attribute_map (buf + ISAKMP_TRANSFORM_SA_ATTRS_OFF, + GET_ISAKMP_GEN_LENGTH (buf) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, + msg->exchange->doi->debug_attribute, msg); + + return 0; +} + +/* Validate the certificate payload P in message MSG. */ +static int +message_validate_cert (struct message *msg, struct payload *p) +{ + if (GET_ISAKMP_CERT_ENCODING (p->p) >= ISAKMP_CERTENC_RESERVED_MIN) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_CERT_ENCODING, 0, 0, 1); + return -1; + } + return 0; +} + +/* Validate the certificate request payload P in message MSG. */ +static int +message_validate_cert_req (struct message *msg, struct payload *p) +{ + struct cert_handler *cert; + size_t len = GET_ISAKMP_GEN_LENGTH (p->p)- ISAKMP_CERTREQ_AUTHORITY_OFF; + + if (GET_ISAKMP_CERTREQ_TYPE (p->p) >= ISAKMP_CERTENC_RESERVED_MIN) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_CERT_ENCODING, 0, 0, 1); + return -1; + } + + /* + * Check the certificate types we support and if an acceptable authority + * is included in the payload check if it can be decoded + */ + if ((cert = cert_get (GET_ISAKMP_CERTREQ_TYPE (p->p))) == NULL || + (len && !cert->certreq_validate (p->p + ISAKMP_CERTREQ_AUTHORITY_OFF, + len))) + { + message_drop (msg, ISAKMP_NOTIFY_CERT_TYPE_UNSUPPORTED, 0, 0, 1); + return -1; + } + return 0; +} + +/* Validate the delete payload P in message MSG. */ +static int +message_validate_delete (struct message *msg, struct payload *p) +{ + u_int8_t proto = GET_ISAKMP_DELETE_PROTO (p->p); + struct doi *doi; + + doi = doi_lookup (GET_ISAKMP_DELETE_DOI (p->p)); + if (!doi) + { + log_print ("message_validate_delete: DOI not supported"); + message_free (msg); + return -1; + } + + if (proto != ISAKMP_PROTO_ISAKMP && doi->validate_proto (proto)) + { + log_print ("message_validate_delete: protocol not supported"); + message_free (msg); + return -1; + } + + /* Validate the SPIs. */ + + return 0; +} + +/* + * Validate the hash payload P in message MSG. */ +static int +message_validate_hash (struct message *msg, struct payload *p) +{ + /* XXX Not implemented yet. */ + return 0; +} + +/* Validate the identification payload P in message MSG. */ +static int +message_validate_id (struct message *msg, struct payload *p) +{ + struct exchange *exchange = msg->exchange; + size_t len = GET_ISAKMP_GEN_LENGTH (p->p); + + if (exchange->doi + && exchange->doi->validate_id_information (GET_ISAKMP_ID_TYPE (p->p), + p->p + ISAKMP_ID_DOI_DATA_OFF, + p->p + ISAKMP_ID_DATA_OFF, + len - ISAKMP_ID_DATA_OFF, + exchange)) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 0, 1); + return -1; + } + return 0; +} + +/* Validate the key exchange payload P in message MSG. */ +static int +message_validate_key_exch (struct message *msg, struct payload *p) +{ + struct exchange *exchange = msg->exchange; + size_t len = GET_ISAKMP_GEN_LENGTH (p->p); + + if (exchange->doi + && exchange->doi->validate_key_information (p->p + ISAKMP_KE_DATA_OFF, + len - ISAKMP_KE_DATA_OFF)) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_KEY_INFORMATION, 0, 0, 1); + return -1; + } + return 0; +} + +/* Validate the nonce payload P in message MSG. */ +static int +message_validate_nonce (struct message *msg, struct payload *p) +{ + /* Nonces require no specific validation. */ + return 0; +} + +/* Validate the notify payload P in message MSG. */ +static int +message_validate_notify (struct message *msg, struct payload *p) +{ + u_int8_t proto = GET_ISAKMP_NOTIFY_PROTO (p->p); + u_int16_t type = GET_ISAKMP_NOTIFY_MSG_TYPE (p->p); + struct doi *doi; + + doi = doi_lookup (GET_ISAKMP_NOTIFY_DOI (p->p)); + if (!doi) + { + log_print ("message_validate_notify: DOI not supported"); + message_free (msg); + return -1; + } + + if (proto != ISAKMP_PROTO_ISAKMP && doi->validate_proto (proto)) + { + log_print ("message_validate_notify: protocol not supported"); + message_free (msg); + return -1; + } + + /* XXX Validate the SPI. */ + + if (type < ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE + || (type >= ISAKMP_NOTIFY_RESERVED_MIN + && type < ISAKMP_NOTIFY_PRIVATE_MIN) + || (type >= ISAKMP_NOTIFY_STATUS_RESERVED1_MIN + && type <= ISAKMP_NOTIFY_STATUS_RESERVED1_MAX) + || (type >= ISAKMP_NOTIFY_STATUS_DOI_MIN + && type <= ISAKMP_NOTIFY_STATUS_DOI_MAX + && doi->validate_notification (type)) + || type >= ISAKMP_NOTIFY_STATUS_RESERVED2_MIN) + { + log_print ("message_validate_notify: message type not supported"); + message_free (msg); + return -1; + } + return 0; +} + +/* Validate the proposal payload P in message MSG. */ +static int +message_validate_proposal (struct message *msg, struct payload *p) +{ + u_int8_t proto = GET_ISAKMP_PROP_PROTO (p->p); + u_int8_t *sa = p->context->p; + + if (proto != ISAKMP_PROTO_ISAKMP + && msg->exchange->doi->validate_proto (proto)) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_PROTOCOL_ID, 0, 0, 1); + return -1; + } + + /* Check that we get monotonically increasing proposal IDs per SA. */ + if (sa != last_sa) + last_sa = sa; + else if (GET_ISAKMP_PROP_NO (p->p) < last_prop_no) + { + message_drop (msg, ISAKMP_NOTIFY_BAD_PROPOSAL_SYNTAX, 0, 0, 1); + return -1; + } + last_prop_no = GET_ISAKMP_PROP_NO (p->p); + + /* XXX Validate the SPI, and other syntactic things. */ + + return 0; +} + +/* + * Validate the SA payload P in message MSG. + * Aside from normal validation, note what DOI is in use for other + * validation routines to look at. Also index the proposal payloads + * on the fly. + * XXX This assumes PAYLOAD_SA is always the first payload + * to be validated, which is true for IKE, except for quick mode where + * a PAYLOAD_HASH comes first, but in that specific case it does not matter. + */ +static int +message_validate_sa (struct message *msg, struct payload *p) +{ + set payload_set; + size_t len; + u_int32_t doi_id; + struct exchange *exchange = msg->exchange; + u_int8_t *pkt = msg->iov[0].iov_base; + + doi_id = GET_ISAKMP_SA_DOI (p->p); + if (!doi_lookup (doi_id)) + { + log_print ("message_validate_sa: DOI not supported"); + message_drop (msg, ISAKMP_NOTIFY_DOI_NOT_SUPPORTED, 0, 0, 1); + return -1; + } + + /* + * It's time to figure out what SA this message is about. If it is + * already set, then we are creating a new phase 1 SA. Otherwise, lookup + * the SA using the cookies and the message ID. If we cannot find + * it, setup a phase 2 SA. + * XXX Is this correct? + */ + if (!exchange) + { + if (zero_test (pkt + ISAKMP_HDR_RCOOKIE_OFF, ISAKMP_HDR_RCOOKIE_LEN)) + exchange = exchange_setup_p1 (msg, doi_id); + else + exchange = exchange_setup_p2 (msg, doi_id); + if (!exchange) + { + /* Log? */ + message_free (msg); + return -1; + } + } + msg->exchange = exchange; + + /* + * Create a struct sa for each SA payload handed to us unless we are the + * inititor where we only will count them. + */ + if (exchange->initiator) + { + /* XXX Count SA payloads. */ + } + else if (sa_create (exchange, msg->transport)) + { + /* XXX Remove exchange if we just created it? */ + message_free (msg); + return -1; + } + + if (exchange->phase == 1) + msg->isakmp_sa = TAILQ_FIRST (&exchange->sa_list); + + /* + * Let the DOI validate the situation, at the same time it tells us what + * the length of the situation field is. + */ + if (exchange->doi->validate_situation (p->p + ISAKMP_SA_SIT_OFF, &len)) + { + log_print ("message_validate_sa: situation not supported"); + message_drop (msg, ISAKMP_NOTIFY_SITUATION_NOT_SUPPORTED, 0, 0, 1); + return -1; + } + + /* Reset the fields we base our proposal & transform number checks on. */ + last_sa = last_prop = 0; + last_prop_no = last_xf_no = 0; + + /* Go through the PROPOSAL payloads. */ + ZERO (&payload_set); + SET (ISAKMP_PAYLOAD_PROPOSAL, &payload_set); + if (message_parse_payloads (msg, p, ISAKMP_PAYLOAD_PROPOSAL, + p->p + ISAKMP_SA_SIT_OFF + len, &payload_set, + message_parse_proposal) == -1) + return -1; + + return 0; +} + +/* Validate the signature payload P in message MSG. */ +static int +message_validate_sig (struct message *msg, struct payload *p) +{ + /* XXX Not implemented yet. */ + return 0; +} + +/* Validate the transform payload P in message MSG. */ +static int +message_validate_transform (struct message *msg, struct payload *p) +{ + u_int8_t proto = GET_ISAKMP_PROP_PROTO (p->context->p); + u_int8_t *prop = p->context->p; + + if (msg->exchange->doi + ->validate_transform_id (proto, GET_ISAKMP_TRANSFORM_ID (p->p))) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_TRANSFORM_ID, 0, 0, 1); + return -1; + } + + /* Check that the reserved field is zero. */ + if (!zero_test (p->p + ISAKMP_TRANSFORM_RESERVED_OFF, + ISAKMP_TRANSFORM_RESERVED_LEN)) + { + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 1); + return -1; + } + + /* + * Check that we get monotonically increasing transform numbers per proposal. + */ + if (prop != last_prop) + last_prop = prop; + else if (GET_ISAKMP_TRANSFORM_NO (p->p) <= last_xf_no) + { + message_drop (msg, ISAKMP_NOTIFY_BAD_PROPOSAL_SYNTAX, 0, 0, 1); + return -1; + } + last_xf_no = GET_ISAKMP_TRANSFORM_NO (p->p); + + /* Validate the attributes. */ + if (attribute_map (p->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF, + GET_ISAKMP_GEN_LENGTH (p->p) + - ISAKMP_TRANSFORM_SA_ATTRS_OFF, + msg->exchange->doi->validate_attribute, msg)) + { + message_drop (msg, ISAKMP_NOTIFY_ATTRIBUTES_NOT_SUPPORTED, 0, 0, 1); + return -1; + } + + return 0; +} + +/* Validate the vendor payload P in message MSG. */ +static int +message_validate_vendor (struct message *msg, struct payload *p) +{ + /* Vendor IDs are only allowed in phase 1. */ + if (msg->exchange->phase != 1) + { + message_drop (msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE, 0, 0, 1); + return -1; + } + + log_debug (LOG_MESSAGE, 40, "message_validate_vendor: vendor ID seen"); + return 0; +} + +/* + * Add an index-record pointing to the payload at BUF in message MSG + * to the PAYLOAD bucket of payloads. This allows us to quickly reference + * payloads by type. Also stash the parent payload P link into the new + * node so we can go from transforms -> payloads -> SAs. + */ +static int +message_index_payload (struct message *msg, struct payload *p, + u_int8_t payload, u_int8_t *buf) +{ + struct payload *payload_node; + + /* Put the payload pointer into the right bucket. */ + payload_node = malloc (sizeof *payload_node); + if (!payload_node) + return -1; + payload_node->p = buf; + payload_node->context = p; + payload_node->flags = 0; + TAILQ_INSERT_TAIL (&msg->payload[payload], payload_node, link); + return 0; +} + +/* + * Group each payload found in MSG by type for easy reference later. + * While doing this, validate the generic parts of the message structure too. + * NEXT is the 1st payload's type. This routine will also register the + * computed message length (i.e. without padding) in msg->iov[0].iov_len. + */ +static int +message_sort_payloads (struct message *msg, u_int8_t next) +{ + set payload_set; + int i, sz; + + ZERO (&payload_set); + for (i = ISAKMP_PAYLOAD_SA; i < ISAKMP_PAYLOAD_RESERVED_MIN; i++) + if (i != ISAKMP_PAYLOAD_PROPOSAL && i != ISAKMP_PAYLOAD_TRANSFORM) + SET (i, &payload_set); + sz = message_parse_payloads (msg, 0, next, + msg->iov[0].iov_base + ISAKMP_HDR_SZ, + &payload_set, message_index_payload); + if (sz == -1) + return -1; + msg->iov[0].iov_len = ISAKMP_HDR_SZ + sz; + SET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base, ISAKMP_HDR_SZ + sz); + return 0; +} + +/* Run all the generic payload tests that the drafts specify. */ +static int +message_validate_payloads (struct message *msg) +{ + int i; + struct payload *p; + + for (i = ISAKMP_PAYLOAD_SA; i < ISAKMP_PAYLOAD_RESERVED_MIN; i++) + for (p = TAILQ_FIRST (&msg->payload[i]); p; p = TAILQ_NEXT (p, link)) + { + log_debug (LOG_MESSAGE, 60, + "message_validate_payloads: payload %s at %p of message %p", + constant_name (isakmp_payload_cst, i), p->p, msg); + field_dump_payload (fields[i - ISAKMP_PAYLOAD_SA], p->p); + if (message_validate_payload[i - ISAKMP_PAYLOAD_SA] (msg, p)) + return -1; + } + return 0; +} + +/* + * All incoming messages go through here. We do generic validity checks + * and try to find or establish SAs. Last but not least we try to find + * the exchange this message, MSG, is part of, and feed it there. + */ +int +message_recv (struct message *msg) +{ + u_int8_t *buf = msg->iov[0].iov_base; + size_t sz = msg->iov[0].iov_len; + u_int8_t exch_type; + int setup_isakmp_sa, msgid_is_zero; + u_int8_t flags; + struct keystate *ks = 0; + + /* Possibly dump a raw hex image of the message to the log channel. */ + message_dump_raw ("message_recv", msg); + + /* Messages shorter than an ISAKMP header are bad. */ + if (sz < ISAKMP_HDR_SZ || sz != GET_ISAKMP_HDR_LENGTH (buf)) + { + log_print ("message_recv: bad message length"); + message_drop (msg, ISAKMP_NOTIFY_UNEQUAL_PAYLOAD_LENGTHS, 0, 0, 1); + return -1; + } + + /* + * If the responder cookie is zero, this is a request to setup an ISAKMP SA. + * Otherwise the cookies should refer to an existing ISAKMP SA. + * + * XXX This is getting ugly, please reread later to see if it can be made + * nicer. + */ + setup_isakmp_sa = zero_test (buf + ISAKMP_HDR_RCOOKIE_OFF, + ISAKMP_HDR_RCOOKIE_LEN); + if (setup_isakmp_sa) + { + /* + * This might be a retransmission of a former ISAKMP SA setup message. + * If so, just drop it. + * XXX Must we really look in both the SA and exchange pools? + */ + if (exchange_lookup_from_icookie (buf + ISAKMP_HDR_ICOOKIE_OFF) + || sa_lookup_from_icookie (buf + ISAKMP_HDR_ICOOKIE_OFF)) + { + log_debug (LOG_MESSAGE, 90, + "message_recv: dropping setup for existing SA"); + message_free (msg); + return -1; + } + } + else + { + msg->isakmp_sa = sa_lookup_by_header (buf, 0); + + /* + * If we cannot find an ISAKMP SA out of the cookies, this is either + * a responder's first reply, and we need to upgrade our exchange, + * or it's just plain invalid cookies. + */ + if (!msg->isakmp_sa) + { + msg->exchange + = exchange_lookup_from_icookie (buf + ISAKMP_HDR_ICOOKIE_OFF); + if (msg->exchange) + exchange_upgrade_p1 (msg); + else + { + log_print ("message_recv: invalid cookie"); + message_drop (msg, ISAKMP_NOTIFY_INVALID_COOKIE, 0, 0, 1); + return -1; + } +#if 0 + msg->isakmp_sa + = sa_lookup_from_icookie (buf + ISAKMP_HDR_ICOOKIE_OFF); + if (msg->isakmp_sa) + sa_isakmp_upgrade (msg); +#endif + } + msg->exchange = exchange_lookup (buf, 1); + } + + if (message_check_duplicate (msg)) + return -1; + + if (GET_ISAKMP_HDR_NEXT_PAYLOAD (buf) >= ISAKMP_PAYLOAD_RESERVED_MIN) + { + log_print ("message_recv: invalid payload type %d in ISAKMP header", + GET_ISAKMP_HDR_NEXT_PAYLOAD (buf)); + message_drop (msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE, 0, 0, 1); + return -1; + } + + /* Validate that the message is of version 1.0. */ + if (ISAKMP_VERSION_MAJOR (GET_ISAKMP_HDR_VERSION (buf)) != 1) + { + log_print ("message_recv: invalid version major %d", + ISAKMP_VERSION_MAJOR (GET_ISAKMP_HDR_VERSION (buf))); + message_drop (msg, ISAKMP_NOTIFY_INVALID_MAJOR_VERSION, 0, 0, 1); + return -1; + } + + if (ISAKMP_VERSION_MINOR (GET_ISAKMP_HDR_VERSION (buf)) != 0) + { + log_print ("message_recv: invalid version minor %d", + ISAKMP_VERSION_MINOR (GET_ISAKMP_HDR_VERSION (buf))); + message_drop (msg, ISAKMP_NOTIFY_INVALID_MINOR_VERSION, 0, 0, 1); + return -1; + } + + /* + * Validate the exchange type. If it's a DOI-specified exchange wait until + * after all payloads have been seen for the validation as the SA payload + * might not yet have been parsed, thus the DOI might be unknown. + */ + exch_type = GET_ISAKMP_HDR_EXCH_TYPE (buf); + if (exch_type == ISAKMP_EXCH_NONE + || (exch_type >= ISAKMP_EXCH_FUTURE_MIN && + exch_type <= ISAKMP_EXCH_FUTURE_MAX) + || (setup_isakmp_sa && exch_type >= ISAKMP_EXCH_DOI_MIN)) + { + log_print ("message_recv: invalid exchange type %s", + constant_name (isakmp_exch_cst, exch_type)); + message_drop (msg, ISAKMP_NOTIFY_INVALID_EXCHANGE_TYPE, 0, 0, 1); + return -1; + } + + /* + * Check for unrecognized flags, or the encryption flag when we don't + * have an ISAKMP SA to decrypt with. + */ + flags = GET_ISAKMP_HDR_FLAGS (buf); + if (flags + & ~(ISAKMP_FLAGS_ENC | ISAKMP_FLAGS_COMMIT | ISAKMP_FLAGS_AUTH_ONLY)) + { + log_print ("message_recv: invalid flags 0x%x", + GET_ISAKMP_HDR_FLAGS (buf)); + message_drop (msg, ISAKMP_NOTIFY_INVALID_FLAGS, 0, 0, 1); + return -1; + } + + /* If we are about to setup an ISAKMP SA, the message ID must be zero. */ + msgid_is_zero = zero_test (buf + ISAKMP_HDR_MESSAGE_ID_OFF, + ISAKMP_HDR_MESSAGE_ID_LEN); + if (setup_isakmp_sa && !msgid_is_zero) + { + log_print ("message_recv: invalid message id"); + message_drop (msg, ISAKMP_NOTIFY_INVALID_MESSAGE_ID, 0, 0, 1); + return -1; + } + + if (!setup_isakmp_sa && msgid_is_zero) + { + msg->exchange = exchange_lookup (buf, 0); + if (!msg->exchange) + { + log_print ("message_recv: phase 1 message after ISAKMP SA is ready"); + + /* + * Retransmit final message of the exchange that set up the + * ISAKMP SA. + */ + if (msg->isakmp_sa->last_sent_in_setup) + { + log_debug (LOG_MESSAGE, 80, + "message_recv: resending last message from phase 1"); + message_send (msg->isakmp_sa->last_sent_in_setup); + } + + return -1; + } + } + + if (flags & ISAKMP_FLAGS_ENC) + { + /* Decrypt rest of message using a DOI-specified IV. */ + ks = msg->isakmp_sa->doi->get_keystate (msg); + if (!ks) + { + message_free (msg); + return -1; + } + msg->orig = malloc (sz); + if (!msg->orig) + { + message_free (msg); + return -1; + } + memcpy (msg->orig, buf, sz); + crypto_decrypt (ks, buf + ISAKMP_HDR_SZ, sz - ISAKMP_HDR_SZ); + } + else + msg->orig = buf; + msg->orig_sz = sz; + + /* + * Check the overall payload structure at the same time as indexing them by + * type. + */ + if (GET_ISAKMP_HDR_NEXT_PAYLOAD (buf) != ISAKMP_PAYLOAD_NONE + && message_sort_payloads (msg, GET_ISAKMP_HDR_NEXT_PAYLOAD (buf))) + { + message_free (msg); + return -1; + } + + /* + * Run generic payload tests now. If anything fails these checks, the + * message needs either to be retained for later duplicate checks or + * freed entirely. + * XXX Should SAs and even transports be cleaned up then too? + */ + if (message_validate_payloads (msg)) + return -1; + + /* + * Now we can validate DOI-specific exchange types. If we have no SA + * DOI-specific exchange types are definitely wrong. + */ + if (exch_type >= ISAKMP_EXCH_DOI_MIN && exch_type <= ISAKMP_EXCH_DOI_MAX + && (!msg->exchange || msg->exchange->doi->validate_exchange (exch_type))) + { + log_print ("message_recv: invalid DOI exchange type %d", exch_type); + message_drop (msg, ISAKMP_NOTIFY_INVALID_EXCHANGE_TYPE, 0, 0, 1); + return -1; + } + + /* If we have not found an exchange by now something is definitely wrong. */ + if (!msg->exchange) + { + log_print ("message_recv: no exchange"); + message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 0, 1); + return -1; + } + + /* Make sure the IV we used gets saved in the proper SA. */ + if (!msg->exchange->keystate && ks) + { + msg->exchange->keystate = ks; + msg->exchange->crypto = ks->xf; + } + + /* Handle the flags. */ + if (flags & ISAKMP_FLAGS_ENC) + msg->exchange->flags |= EXCHANGE_FLAG_ENCRYPT; + if ((msg->exchange->flags & EXCHANGE_FLAG_COMMITTED) == 0 + && (flags & ISAKMP_FLAGS_COMMIT)) + msg->exchange->flags |= EXCHANGE_FLAG_HE_COMMITTED; + + /* OK let the exchange logic do the rest. */ + exchange_run (msg); + + return 0; +} + +/* Queue up message MSG for transmittal. */ +void +message_send (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + + /* Reset the retransmission event. */ + msg->retrans = 0; + + /* + * If the ISAKMP SA has set up encryption, encrypt the message. + * However, in a retransmit, it is already encrypted. + */ + if ((msg->flags & MSG_ENCRYPTED) == 0 + && exchange->flags & EXCHANGE_FLAG_ENCRYPT) + { + if (!exchange->keystate) + { + exchange->keystate = exchange->doi->get_keystate (msg); + exchange->crypto = exchange->keystate->xf; + exchange->flags |= EXCHANGE_FLAG_ENCRYPT; + } + + if (message_encrypt (msg)) + { + /* XXX Log. */ + return; + } + } + + /* Keep the COMMIT bit on. */ + if (exchange && exchange->flags & EXCHANGE_FLAG_COMMITTED) + SET_ISAKMP_HDR_FLAGS (msg->iov[0].iov_base, + GET_ISAKMP_HDR_FLAGS (msg->iov[0].iov_base) + | ISAKMP_FLAGS_COMMIT); + + message_dump_raw ("message_send", msg); + TAILQ_INSERT_TAIL (&msg->transport->sendq, msg, link); +} + +/* + * Setup the ISAKMP message header for message MSG. EXCHANGE is the exchange + * type, FLAGS are the ISAKMP header flags and MSG_ID is message ID + * identifying the exchange. + */ +void +message_setup_header (struct message *msg, u_int8_t exchange, u_int8_t flags, + u_int8_t *msg_id) +{ + u_int8_t *buf = msg->iov[0].iov_base; + + SET_ISAKMP_HDR_ICOOKIE (buf, msg->exchange->cookies); + SET_ISAKMP_HDR_RCOOKIE (buf, + msg->exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN); + SET_ISAKMP_HDR_NEXT_PAYLOAD (buf, ISAKMP_PAYLOAD_NONE); + SET_ISAKMP_HDR_VERSION (buf, ISAKMP_VERSION_MAKE (1, 0)); + SET_ISAKMP_HDR_EXCH_TYPE (buf, exchange); + SET_ISAKMP_HDR_FLAGS (buf, flags); + SET_ISAKMP_HDR_MESSAGE_ID (buf, msg_id); + SET_ISAKMP_HDR_LENGTH (buf, msg->iov[0].iov_len); +} + +/* + * Add the payload of type PAYLOAD in BUF sized SZ to the MSG message. + * The caller thereby is released from the responsibility of freeing BUF, + * unless we return a failure of course. If LINK is set the former + * payload's "next payload" field to PAYLOAD. + * + * XXX We might want to resize the iov array several slots at a time. + */ +int +message_add_payload (struct message *msg, u_int8_t payload, u_int8_t *buf, + size_t sz, int link) +{ + struct iovec *new_iov; + struct payload *payload_node; + + payload_node = malloc (sizeof *payload_node); + if (!payload_node) + return -1; + new_iov + = (struct iovec *)realloc (msg->iov, (msg->iovlen + 1) * sizeof *msg->iov); + if (!new_iov) + { + free (payload_node); + return -1; + } + msg->iov = new_iov; + new_iov[msg->iovlen].iov_base = buf; + new_iov[msg->iovlen].iov_len = sz; + msg->iovlen++; + if (link) + *msg->nextp = payload; + msg->nextp = buf + ISAKMP_GEN_NEXT_PAYLOAD_OFF; + *msg->nextp = ISAKMP_PAYLOAD_NONE; + SET_ISAKMP_GEN_RESERVED (buf, 0); + SET_ISAKMP_GEN_LENGTH (buf, sz); + SET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base, + GET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base) + sz); + + /* + * For the sake of exchange_validate we index the payloads even in outgoing + * messages, however context and flags are uninteresting in this situation. + */ + payload_node->p = buf; + TAILQ_INSERT_TAIL (&msg->payload[payload], payload_node, link); + return 0; +} + +/* XXX Move up when ready. */ +struct info_args { + char discr; + u_int32_t doi; + u_int8_t proto; + u_int16_t spi_sz; + union { + struct { + u_int16_t msg_type; + u_int8_t *spi; + } n; + struct { + u_int16_t nspis; + u_int8_t **spi; + } d; + } u; +}; + +/* + * As a reaction to the incoming message MSG create an informational exchange + * protected by ISAKMP_SA and send a notify payload of type NOTIFY, with + * fields initialized from SA. + * + * XXX Should we handle sending multiple notify payloads? The draft allows + * it, but do we need it? Furthermore, should we not return a success + * status value? + */ +void +message_send_notification (struct message *msg, struct sa *isakmp_sa, + u_int16_t notify, struct proto *proto, + int initiator) +{ + struct info_args args; + + args.discr = 'N'; + args.doi = proto ? proto->sa->doi->id : 0; + args.proto = proto ? proto->proto : 0; + args.spi_sz = proto ? proto->spi_sz[initiator] : 0; + args.u.n.msg_type = notify; + args.u.n.spi = proto ? proto->spi[initiator] : 0; + if (isakmp_sa->flags & SA_FLAG_READY) + exchange_establish_p2 (isakmp_sa, ISAKMP_EXCH_INFO, &args); + else + exchange_establish_p1 (msg->transport, ISAKMP_EXCH_INFO, + msg->exchange->doi->id, &args); +} + +void +message_send_info (struct message *msg) +{ + u_int8_t *buf; + size_t sz; + struct info_args *args = msg->extra; + u_int8_t payload; + + sz = (args->discr == 'N' ? ISAKMP_NOTIFY_SPI_OFF + args->spi_sz + : ISAKMP_DELETE_SPI_OFF + args->u.d.nspis * args->spi_sz); + buf = calloc (1, sz); + switch (args->discr) + { + case 'N': + /* Build the NOTIFY payload. */ + payload = ISAKMP_PAYLOAD_NOTIFY; + SET_ISAKMP_NOTIFY_DOI (buf, args->doi); + SET_ISAKMP_NOTIFY_PROTO (buf, args->proto); + SET_ISAKMP_NOTIFY_SPI_SZ (buf, args->spi_sz); + SET_ISAKMP_NOTIFY_MSG_TYPE (buf, args->u.n.msg_type); + memcpy (buf + ISAKMP_NOTIFY_SPI_OFF, args->u.n.spi, args->spi_sz); + break; + + case 'D': + default: /* Silence GCC. */ + /* Build the DELETE payload. */ + payload = ISAKMP_PAYLOAD_DELETE; + SET_ISAKMP_DELETE_DOI (buf, args->doi); + SET_ISAKMP_DELETE_PROTO (buf, args->proto); + SET_ISAKMP_DELETE_SPI_SZ (buf, args->spi_sz); + SET_ISAKMP_DELETE_NSPIS (buf, args->u.d.nspis); + memcpy (buf + ISAKMP_DELETE_SPI_OFF, args->u.d.spi, + args->u.d.nspis * args->spi_sz); + break; + } + + if (message_add_payload (msg, payload, buf, sz, 1)) + { + /* Log! */ + message_free (msg); + return; + } +} + +/* + * Drop the MSG message due to reason given in NOTIFY. If NOTIFY is set + * send out a notification to the originator. + */ +void +message_drop (struct message *msg, int notify, struct proto *proto, + int initiator, int clean) +{ + struct transport *t = msg->transport; + struct sockaddr *dst; + int dst_len; + + t->vtbl->get_dst (t, &dst, &dst_len); + + /* XXX Assumes IPv4. */ + log_print ("dropped message from %s due to notification type %s", + inet_ntoa (((struct sockaddr_in *)dst)->sin_addr), + constant_name (isakmp_notify_cst, notify)); + + /* + * If specified and we have at least an ISAKMP SA, return a notification. + * XXX how to setup my SA. + */ + if (notify && msg->isakmp_sa) + message_send_notification (msg, msg->isakmp_sa, notify, proto, initiator); + if (clean) + message_free (msg); +} + +/* + * If the user demands debug printouts, printout MSG with as much detail + * as we can without resorting to per-payload handling. + */ +static void +message_dump_raw (char *header, struct message *msg) +{ + int i, j, k = 0; + char buf[80], *p = buf; + + /* XXX Should we use LOG_MESSAGE instead? */ + log_debug (LOG_TRANSPORT, 10, "%s: message %p", header, msg); + field_dump_payload (isakmp_hdr_fld, msg->iov[0].iov_base); + for (i = 0; i < msg->iovlen; i++) + for (j = 0; j < msg->iov[i].iov_len; j++) + { + sprintf(p, "%02x", ((u_int8_t *)msg->iov[i].iov_base)[j]); + p += 2; + if (++k % 32 == 0) + { + *p = '\0'; + log_debug (LOG_TRANSPORT, 10, "%s", buf); + p = buf; + } + else if (k % 4 == 0) + *p++ = ' '; + } + *p = '\0'; + if (p != buf) + log_debug (LOG_TRANSPORT, 10, "%s", buf); +} + +/* + * Encrypt an outgoing message MSG. As outgoing messages are represented + * with an iovec with one segment per payload, we need to coalesce them + * into just une buffer containing all payloads and some padding before + * we encrypt. + */ +static int +message_encrypt (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + size_t sz = 0; + u_int8_t *buf; + int i; + + /* If no payloads, nothing to do. */ + if (msg->iovlen == 1) + return 0; + + /* + * For encryption we need to put all payloads together in a single buffer. + * This buffer should be padded to the current crypto transform's blocksize. + */ + for (i = 1; i < msg->iovlen; i++) + sz += msg->iov[i].iov_len; + sz = ((sz + exchange->crypto->blocksize - 1) / exchange->crypto->blocksize) + * exchange->crypto->blocksize; + buf = realloc (msg->iov[1].iov_base, sz); + if (!buf) + return -1; + msg->iov[1].iov_base = buf; + for (i = 2; i < msg->iovlen; i++) + { + memcpy (buf + msg->iov[1].iov_len, msg->iov[i].iov_base, + msg->iov[i].iov_len); + msg->iov[1].iov_len += msg->iov[i].iov_len; + free (msg->iov[i].iov_base); + } + + /* Pad with zeroes. */ + memset (buf + msg->iov[1].iov_len, '\0', sz - msg->iov[1].iov_len); + msg->iov[1].iov_len = sz; + msg->iovlen = 2; + + SET_ISAKMP_HDR_FLAGS (msg->iov[0].iov_base, + GET_ISAKMP_HDR_FLAGS (msg->iov[0].iov_base) + | ISAKMP_FLAGS_ENC); + SET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base, ISAKMP_HDR_SZ + sz); + crypto_encrypt (exchange->keystate, buf, msg->iov[1].iov_len); + msg->flags |= MSG_ENCRYPTED; + + /* Update the IV so we can decrypt the next incoming message. */ + crypto_update_iv (exchange->keystate); + + return 0; +} + +/* + * Check whether the message MSG is a duplicate of the last one negotiating + * this specific SA. + */ +static int +message_check_duplicate (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + size_t sz = msg->iov[0].iov_len; + u_int8_t *pkt = msg->iov[0].iov_base; + + /* If no SA has been found, we cannot test, thus it's good. */ + if (!exchange) + return 0; + + log_debug (LOG_MESSAGE, 90, "message_check_duplicate: last_received 0x%x", + exchange->last_received); + if (exchange->last_received) + { + log_debug_buf (LOG_MESSAGE, 95, "message_check_duplicate: last_received", + exchange->last_received->orig, + exchange->last_received->orig_sz); + /* Is it a duplicate, lose the new one. */ + if (sz == exchange->last_received->orig_sz + && memcmp (pkt, exchange->last_received->orig, sz) == 0) + { + log_debug (LOG_MESSAGE, 80, "message_check_duplicate: dropping dup"); + /* XXX Should we do an early retransmit of our last message? */ + message_free (msg); + return -1; + } + } + + /* + * As this new message is an indication that state is moving forward + * at the peer, remove the retransmit timer on our last message. + */ + if (exchange->last_sent) + { + message_free (exchange->last_sent); + exchange->last_sent = 0; + } + + return 0; +} + +/* Helper to message_negotiate_sa. */ +static INLINE struct payload * +step_transform (struct payload *tp, struct payload **propp, + struct payload **sap) +{ + tp = TAILQ_NEXT (tp, link); + if (tp) + { + *propp = tp->context; + *sap = (*propp)->context; + } + return tp; +} + +/* + * Pick out the first transforms out of MSG (which should contain at least one + * SA payload) we accept as a full protection suite. + */ +int +message_negotiate_sa (struct message *msg) +{ + struct payload *tp, *propp, *sap, *next_tp = 0, *next_propp, *next_sap; + struct sa *sa; + struct proto *proto; + int suite_ok_so_far = 0; + struct exchange *exchange = msg->exchange; + u_int8_t *spi; + size_t spi_sz; + + /* + * This algorithm is a weird bottom-up thing... mostly due to the + * payload links pointing upwards. + * + * The algorithm goes something like this: + * Foreach transform + * If transform is compatible + * Remember that this protocol can work + * Skip to last transform of this protocol + * If next transform belongs to a new protocol inside the same suite + * If no transform was found for the current protocol + * Forget all earlier transforms for protocols in this suite + * Skip to last transform of this suite + * If next transform belongs to a new suite + * If the current protocol had an OK transform + * Skip to the last transform of this SA + * If the next transform belongs to a new SA + * If no transforms have been chosen + * Issue a NO_PROPOSAL_CHOSEN notification + */ + + sa = TAILQ_FIRST (&exchange->sa_list); + for (tp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); tp; + tp = next_tp) + { + propp = tp->context; + sap = propp->context; + sap->flags |= PL_MARK; + next_tp = step_transform (tp, &next_propp, &next_sap); + + /* For each transform, see if it is compatible. */ + if (!attribute_map (tp->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF, + GET_ISAKMP_GEN_LENGTH (tp->p) + - ISAKMP_TRANSFORM_SA_ATTRS_OFF, + exchange->doi->is_attribute_incompatible, msg)) + { + log_debug (LOG_MESSAGE, 30, + "message_negotiate_sa: " + "transform %d proto %d proposal %d ok", + GET_ISAKMP_TRANSFORM_NO (tp->p), + GET_ISAKMP_PROP_PROTO (propp->p), + GET_ISAKMP_PROP_NO (propp->p)); + if (sa_add_transform (sa, tp, exchange->initiator, &proto)) + goto cleanup; + suite_ok_so_far = 1; + + /* Skip to last transform of this protocol proposal. */ + while ((next_tp = step_transform (tp, &next_propp, &next_sap)) + && next_propp == propp) + tp = next_tp; + } + + /* + * Figure out if we will be looking at a new protocol proposal + * inside the current protection suite. + */ + if (next_tp && propp != next_propp && sap == next_sap + && (GET_ISAKMP_PROP_NO (propp->p) + == GET_ISAKMP_PROP_NO (next_propp->p))) + { + if (!suite_ok_so_far) + { + log_debug (LOG_MESSAGE, 30, + "message_negotiate_sa: proto %d proposal %d failed", + GET_ISAKMP_PROP_PROTO (propp->p), + GET_ISAKMP_PROP_NO (propp->p)); + /* Remove potentially succeeded choices from the SA. */ + while (TAILQ_FIRST (&sa->protos)) + TAILQ_REMOVE (&sa->protos, TAILQ_FIRST (&sa->protos), link); + + /* Skip to the last transform of this protection suite. */ + while ((next_tp = step_transform (tp, &next_propp, &next_sap)) + && (GET_ISAKMP_PROP_NO (next_propp->p) + == GET_ISAKMP_PROP_NO (propp->p)) + && next_sap == sap) + tp = next_tp; + } + suite_ok_so_far = 0; + } + + /* Figure out if we will be looking at a new protection suite. */ + if (!next_tp + || (propp != next_propp + && (GET_ISAKMP_PROP_NO (propp->p) + != GET_ISAKMP_PROP_NO (next_propp->p))) + || sap != next_sap) + { + /* + * Check if the suite we just considered was OK, if so we're done. + */ + if (suite_ok_so_far) + { + log_debug (LOG_MESSAGE, 30, + "message_negotiate_sa: proposal %d succeeded", + GET_ISAKMP_PROP_NO (propp->p)); + + /* Record the other guy's SPI. */ + spi_sz = GET_ISAKMP_PROP_SPI_SZ (propp->p); + if (spi_sz) + { + spi = malloc (spi_sz); + if (!spi) + goto cleanup; + memcpy (spi, propp->p + ISAKMP_PROP_SPI_OFF, spi_sz); + } + else + spi = 0; + TAILQ_FIRST (&sa->protos)->spi[!msg->exchange->initiator] + = spi; + log_debug_buf (LOG_MESSAGE, 40, "message_negotiate_sa: SPI", spi, + spi_sz); + + /* Skip to the last transform of this SA. */ + while ((next_tp = step_transform (tp, &next_propp, &next_sap)) + && next_sap == sap) + tp = next_tp; + } + } + + /* Have we walked all the proposals of an SA? */ + if (!next_tp || sap != next_sap) + { + if (!suite_ok_so_far) + { + /* + * XXX We cannot possibly call this a drop... seeing we just turn + * down one of the offers, can we? I suggest renaming + * message_drop to something else. + */ + message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 0, 0); + } + sa = TAILQ_NEXT (sa, next); + } + } + return 0; + + cleanup: + /* Remove potentially succeeded choices from the SA. */ + while (TAILQ_FIRST (&sa->protos)) + TAILQ_REMOVE (&sa->protos, TAILQ_FIRST (&sa->protos), link); + return -1; +} + +int +message_add_sa_payload (struct message *msg) +{ + struct exchange *exchange = msg->exchange; + u_int8_t *sa_buf, *saved_nextp_sa, *saved_nextp_prop; + size_t sa_len, extra_sa_len; + int i, nprotos = 0; + struct proto *proto; + u_int8_t **transforms = 0, **proposals = 0; + size_t *transform_lens = 0, *proposal_lens = 0; + struct sa *sa; + struct doi *doi = exchange->doi; + u_int8_t *spi = 0; + size_t spi_sz; + + /* + * Generate SA payloads. + */ + for (sa = TAILQ_FIRST (&exchange->sa_list); sa; + sa = TAILQ_NEXT (sa, next)) + { + /* Setup a SA payload. */ + sa_len = ISAKMP_SA_SIT_OFF + doi->situation_size (); + extra_sa_len = 0; + sa_buf = malloc (sa_len); + if (!sa_buf) + goto cleanup; + SET_ISAKMP_SA_DOI (sa_buf, doi->id); + doi->setup_situation (sa_buf); + + /* Count transforms. */ + nprotos = 0; + for (proto = TAILQ_FIRST (&sa->protos); proto; + proto = TAILQ_NEXT (proto, link)) + nprotos++; + + /* Allocate transient transform and proposal payload/size vectors. */ + transforms = calloc (nprotos, sizeof *transforms); + if (!transforms) + goto cleanup; + transform_lens = calloc (nprotos, sizeof *transform_lens); + if (!transform_lens) + goto cleanup; + proposals = calloc (nprotos, sizeof *proposals); + if (!proposals) + goto cleanup; + proposal_lens = calloc (nprotos, sizeof *proposal_lens); + if (!proposal_lens) + goto cleanup; + + /* Pick out the chosen transforms. */ + for (proto = TAILQ_FIRST (&sa->protos), i = 0; proto; + proto = TAILQ_NEXT (proto, link), i++) + { + transform_lens[i] = GET_ISAKMP_GEN_LENGTH (proto->chosen->p); + transforms[i] = malloc (transform_lens[i]); + if (!transforms[i]) + goto cleanup; + + /* Get SPI from application. */ + if (doi->get_spi) + { + spi = doi->get_spi (&spi_sz, + GET_ISAKMP_PROP_PROTO (proto->chosen + ->context->p), + msg); + if (spi_sz && !spi) + goto cleanup; + proto->spi[exchange->initiator] = spi; + proto->spi_sz[exchange->initiator] = spi_sz; + } + else + spi_sz = 0; + + proposal_lens[i] = ISAKMP_PROP_SPI_OFF + spi_sz; + proposals[i] = malloc (proposal_lens[i]); + if (!proposals[i]) + goto cleanup; + + memcpy (transforms[i], proto->chosen->p, transform_lens[i]); + memcpy (proposals[i], proto->chosen->context->p, + ISAKMP_PROP_SPI_OFF); + SET_ISAKMP_PROP_NTRANSFORMS (proposals[i], 1); + SET_ISAKMP_PROP_SPI_SZ (proposals[i], spi_sz); + if (spi_sz) + memcpy (proposals[i] + ISAKMP_PROP_SPI_OFF, spi, spi_sz); + free (spi); + spi = 0; + extra_sa_len += proposal_lens[i] + transform_lens[i]; + } + + /* + * Add the payloads. As this is a SA, we need to recompute the + * lengths of the payloads containing others. We also need to + * reset these payload's "next payload type" field. + */ + if (message_add_payload (msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) + goto cleanup; + SET_ISAKMP_GEN_LENGTH (sa_buf, sa_len + extra_sa_len); + sa_buf = 0; + + saved_nextp_sa = msg->nextp; + for (proto = TAILQ_FIRST (&sa->protos), i = 0; proto; + proto = TAILQ_NEXT (proto, link), i++) + { + if (message_add_payload (msg, ISAKMP_PAYLOAD_PROPOSAL, proposals[i], + proposal_lens[i], i > 1)) + goto cleanup; + SET_ISAKMP_GEN_LENGTH (proposals[i], + proposal_lens[i] + transform_lens[i]); + proposals[i] = 0; + + saved_nextp_prop = msg->nextp; + if (message_add_payload (msg, ISAKMP_PAYLOAD_TRANSFORM, + transforms[i], transform_lens[i], 0)) + goto cleanup; + msg->nextp = saved_nextp_prop; + transforms[i] = 0; + } + msg->nextp = saved_nextp_sa; + + /* Free the temporary allocations made above. */ + free (transforms); + free (transform_lens); + free (proposals); + free (proposal_lens); + } + return 0; + + cleanup: + if (spi) + free (spi); + if (sa_buf) + free (sa_buf); + for (i = 0; i < nprotos; i++) + { + if (transforms[i]) + free (transforms[i]); + if (proposals[i]) + free (proposals[i]); + } + if (transforms) + free (transforms); + if (transform_lens) + free (transform_lens); + if (proposals) + free (proposals); + if (proposal_lens) + free (proposal_lens); + return -1; +} + +/* + * Return a copy of MSG's constants starting from OFFSET and stash the size + * in SZP. It is the callers responsibility to free this up. + */ +u_int8_t * +message_copy (struct message *msg, size_t offset, size_t *szp) +{ + int i, skip = -1; + size_t sz = 0; + ssize_t start = -1; + u_int8_t *buf, *p; + + /* Calculate size of message and where we want to start to copy. */ + for (i = 1; i < msg->iovlen; i++) + { + sz += msg->iov[i].iov_len; + if (sz <= offset) + skip = i; + else if (start < 0) + start = offset - (sz - msg->iov[i].iov_len); + } + + /* Allocate and copy. */ + *szp = sz - offset; + buf = malloc (*szp); + if (!buf) + return 0; + p = buf; + for (i = skip + 1; i < msg->iovlen; i++) + { + memcpy (p, msg->iov[i].iov_base + start, msg->iov[i].iov_len - start); + p += msg->iov[i].iov_len - start; + start = 0; + } + return buf; +} + +/* Register a post-send function POST_SEND with message MSG. */ +int +message_register_post_send (struct message *msg, + void (*post_send) (struct message *)) +{ + struct post_send *node; + + node = malloc (sizeof *node); + if (!node) + return -1; + node->func = post_send; + TAILQ_INSERT_TAIL (&msg->post_send, node, link); + return 0; +} + +/* Run the post-send functions of message MSG. */ +void +message_post_send (struct message *msg) +{ + struct post_send *node; + + while ((node = TAILQ_FIRST (&msg->post_send)) != 0) + { + TAILQ_REMOVE (&msg->post_send, node, link); + node->func (msg); + free (node); + } +} diff --git a/sbin/isakmpd/message.h b/sbin/isakmpd/message.h new file mode 100644 index 00000000000..30d202148b9 --- /dev/null +++ b/sbin/isakmpd/message.h @@ -0,0 +1,178 @@ +/* $Id: message.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _MESSAGE_H_ +#define _MESSAGE_H_ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include "isakmp.h" + +struct event; +struct message; +struct proto; +struct sa; +struct transport; + +struct payload { + /* Link all payloads of the same type through here. */ + TAILQ_ENTRY (payload) link; + + /* The pointer to the actual payload data. */ + u_int8_t *p; + + /* + * A pointer to the parent payload, used for proposal and transform payloads. + */ + struct payload *context; + + /* Payload flags described below. */ + int flags; +}; + +/* Payload flags. */ + +/* + * Set this when a payload has been handled, so we later can sweep over + * unhandled ones. + */ +#define PL_MARK 1 + +/* A post-send chain of functions to be called. */ +struct post_send { + /* Link to the next function in the chain. */ + TAILQ_ENTRY (post_send) link; + + /* The actual function. */ + void (*func) (struct message *); +}; + +struct message { + /* Link message in send queues via this link. */ + TAILQ_ENTRY (message) link; + + /* Message flags described below. */ + u_int flags; + + /* + * This is the transport the message either arrived on or will be sent to. + */ + struct transport *transport; + + /* + * This is the ISAKMP SA protecting this message. + * XXX Needs to be redone to some keystate pointer or something. + */ + struct sa *isakmp_sa; + + /* This is the exchange where this message appears. */ + struct exchange *exchange; + + /* + * A segmented buffer structure holding the messages raw contents. On input + * only segment 0 will be filled, holding all of the message. On output, as + * long as the message body is unencrypted each segment will be one payload, + * after encryption segment 0 will be the unencryptd header, and segment 1 + * will be the encrypted payloads, all of them. + */ + struct iovec *iov; + + /* The segment count. */ + u_int iovlen; + + /* Pointer to the last "next payload" field. */ + u_int8_t *nextp; + + /* "Smart" pointers to each payload, sorted by type. */ + TAILQ_HEAD (payload_head, payload) payload[ISAKMP_PAYLOAD_RESERVED_MIN]; + + /* Number of times this message has been sent. */ + int xmits; + + /* The timeout event causing retransmission of this message. */ + struct event *retrans; + + /* The (possibly encrypted) message text, used for duplicate testing. */ + u_int8_t *orig; + size_t orig_sz; + + /* + * Extra baggage needed to travel with the message. Used transiently + * in context sensitive ways. + */ + void *extra; + + /* + * Hooks for stuff needed to be done after the message has gone out to + * the wire. + */ + TAILQ_HEAD (post_send_head, post_send) post_send; +}; + +/* Message flags. */ + +/* Don't retransmit this message, ever. */ +#define MSG_NO_RETRANS 1 + +/* Don't free message after sending */ +#define MSG_KEEP 2 + +/* The message has already been encrypted. */ +#define MSG_ENCRYPTED 4 + +extern int message_add_payload (struct message *, u_int8_t, u_int8_t *, + size_t, int); +extern int message_add_sa_payload (struct message *); +extern struct message *message_alloc (struct transport *, u_int8_t *, size_t); +extern struct message *message_alloc_reply (struct message *); +extern u_int8_t *message_copy (struct message *, size_t, size_t *); +extern void message_drop (struct message *, int, struct proto *, int, int); +extern void message_free (struct message *); +extern int message_negotiate_sa (struct message *); +extern int message_recv (struct message *); +extern int message_register_post_send (struct message *, + void (*) (struct message *)); +extern void message_post_send (struct message *); +extern void message_send (struct message *); +extern void message_send_info (struct message *); +extern void message_send_notification (struct message *, struct sa *, + u_int16_t, struct proto *, int); +extern void message_setup_header (struct message *, u_int8_t, u_int8_t, + u_int8_t *); + +#endif /* _MESSAGE_H_ */ diff --git a/sbin/isakmpd/pf_encap.c b/sbin/isakmpd/pf_encap.c new file mode 100644 index 00000000000..9f572036fed --- /dev/null +++ b/sbin/isakmpd/pf_encap.c @@ -0,0 +1,654 @@ +/* $Id: pf_encap.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <net/encap.h> +#include <netinet/ip_ah.h> +#include <netinet/ip_esp.h> +#include <netinet/ip_ip4.h> +#include <netinet/ip_ipsp.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "app.h" +#include "conf.h" +#include "hash.h" +#include "ipsec.h" +#include "ipsec_num.h" +#include "isakmp.h" +#include "log.h" +#include "pf_encap.h" +#include "sa.h" +#include "sysdep.h" +#include "timer.h" +#include "transport.h" + +void pf_encap_request_sa (struct encap_msghdr *); + +int +pf_encap_open () +{ + int fd; + + fd = socket (PF_ENCAP, SOCK_RAW, PF_UNSPEC); + if (fd == -1) + { + log_error ("pf_encap_open: " + "socket (PF_ENCAP, SOCK_RAW, PF_UNSPEC) failed"); + return -1; + } + return fd; +} + +static void +pf_encap_expire (struct encap_msghdr *emsg) +{ + /* XXX not implemented yet. */ +} + +static void +pf_encap_notify (struct encap_msghdr *emsg) +{ + log_debug_buf (LOG_PF_ENCAP, 90, "pf_encap_notify: emsg", (u_int8_t *)emsg, + sizeof *emsg); + + switch (emsg->em_not_type) + { + case NOTIFY_SOFT_EXPIRE: + case NOTIFY_HARD_EXPIRE: + log_debug (LOG_PF_ENCAP, 20, + "pf_encap_handler: NOTIFY_%s_EXPIRE dst %s spi %x sproto %d", + emsg->em_not_type == NOTIFY_SOFT_EXPIRE ? "SOFT" : "HARD", + inet_ntoa (emsg->em_not_dst), emsg->em_not_spi, + emsg->em_not_sproto); + pf_encap_expire (emsg); + break; + + case NOTIFY_REQUEST_SA: + log_debug (LOG_PF_ENCAP, 10, + "pf_encap_handler: SA requested for %s type %d", + inet_ntoa (emsg->em_not_dst), emsg->em_not_satype); + pf_encap_request_sa (emsg); + break; + + default: + log_print ("pf_encap_handler: unknown notify message type (%d)", + emsg->em_not_type); + break; + } + free (emsg); +} + +void +pf_encap_handler (int fd) +{ + u_int8_t *buf; + struct encap_msghdr *emsg; + ssize_t len; + + /* + * PF_ENCAP version 1 has a max length equal to the notify length on + * upcoming packets. + */ + buf = malloc (EMT_NOTIFY_FLEN); + if (!buf) + return; + emsg = (struct encap_msghdr *)buf; + + len = read (fd, buf, EMT_NOTIFY_FLEN); + if (len == -1) + { + log_error ("pf_encap_handler: read (%d, ...) failed", fd); + free (emsg); + return; + } + + if (emsg->em_version != PFENCAP_VERSION_1) + { + log_print ("pf_encap_handler: " + "unexpected message version (%d) from PF_ENCAP socket", + emsg->em_version); + free (emsg); + return; + } + + if (emsg->em_type != EMT_NOTIFY) + { + log_print ("pf_encap_handler: " + "unexpected message type (%d) from PF_ENCAP socket", + emsg->em_type); + free (emsg); + return; + } + + pf_encap_notify (emsg); +} + +void +pf_encap_request_sa (struct encap_msghdr *emsg) +{ + struct transport *transport; + struct sa *isakmp_sa; + char addr[20]; + in_port_t port; + struct sockaddr *taddr; + int taddr_len; + + /* + * XXX I'd really want some more flexible way to map the IPv4 address in + * this message to a general transport endpoint. For now we hardcode + * the ISAKMP peer to be at the same IP and talking UDP. + */ + port = conf_get_num (inet_ntoa (emsg->em_not_dst), "port"); + if (!port) + port = UDP_DEFAULT_PORT; + snprintf (addr, 20, "%s:%d", inet_ntoa (emsg->em_not_dst), port); + transport = transport_create ("udp", addr); + if (!transport) + { + log_print ("pf_encap_request_sa: " + "transport \"udp %s\" could not be created", + transport, addr); + return; + } + + /* Check if we already have an ISAKMP SA setup. */ + transport->vtbl->get_dst (transport, &taddr, &taddr_len); + isakmp_sa = sa_isakmp_lookup_by_peer (taddr, taddr_len); + if (!isakmp_sa) + /* XXX transport_free (transport) */ ; +} + +/* Write a PF_ENCAP request down to the kernel. */ +static int +pf_encap_write (struct encap_msghdr *em) +{ + ssize_t n; + + em->em_version = PFENCAP_VERSION_1; + + log_debug_buf (LOG_PF_ENCAP, 30, "pf_encap_write: em", (u_int8_t *)em, + em->em_msglen); + n = write (app_socket, em, em->em_msglen); + if (n == -1) + { + log_error ("write (%d, ...) failed", app_socket); + return -1; + } + if ((size_t)n != em->em_msglen) + { + log_error ("write (%d, ...) returned prematurely", app_socket); + return -1; + } + return 0; +} + +/* + * Read a PF_ENCAP non-notify packet (e.g. an answer to a request of ours) + * If we see a notify queue it up as a timeout timing out NOW for the main + * loop to see. + */ +static struct encap_msghdr * +pf_encap_read () +{ + u_int8_t *buf; + ssize_t n; + struct encap_msghdr *emsg; + struct timeval now; + + /* + * PF_ENCAP version 1 has a max length equal to the notify length on + * upcoming packets. + */ + buf = malloc (EMT_NOTIFY_FLEN); + if (!buf) + goto cleanup; + emsg = (struct encap_msghdr *)buf; + + while (1) + { + /* XXX Should we have a static pf_encap_socket instead? */ + n = read (app_socket, buf, EMT_NOTIFY_FLEN); + if (n == -1) + { + log_error ("read (%d, ...) failed", app_socket); + goto cleanup; + } + + if ((size_t)n < EMT_GENLEN || (size_t)n != emsg->em_msglen) + { + log_print ("read (%d, ...) returned short packet (%d bytes)", + app_socket, n); + goto cleanup; + } + + /* We drop all messages that is not what we expect. */ + if (emsg->em_version != PFENCAP_VERSION_1) + continue; + + /* + * Enqueue notifications so they will be dealt with as soon as we get + * back to the main server loop. + */ + if (emsg->em_type == EMT_NOTIFY) + { + gettimeofday (&now, 0); + timer_add_event ("pf_encap_notify", + (void (*) (void *))pf_encap_notify, emsg, &now); + + /* We need a new buffer since we gave our former one away. */ + buf = malloc (EMT_NOTIFY_FLEN); + if (!buf) + goto cleanup; + emsg = (struct encap_msghdr *)buf; + continue; + } + + return emsg; + } + + cleanup: + if (buf) + free (buf); + return 0; +} + +u_int8_t * +pf_encap_get_spi (size_t *sz, u_int8_t proto, void *id, size_t id_sz) +{ + struct encap_msghdr *emsg = 0; + u_int8_t *spi = 0; + struct sockaddr_in *ipv4_id = id; + + emsg = calloc (1, EMT_RESERVESPI_FLEN); + if (!emsg) + return 0; + + emsg->em_msglen = EMT_RESERVESPI_FLEN; + emsg->em_type = EMT_RESERVESPI; + emsg->em_gen_spi = 0; + memcpy (&emsg->em_gen_dst, &ipv4_id->sin_addr, sizeof ipv4_id->sin_addr); + emsg->em_gen_sproto = + proto == IPSEC_PROTO_IPSEC_ESP ? IPPROTO_ESP : IPPROTO_AH; + + if (pf_encap_write (emsg)) + goto cleanup; + free (emsg); + emsg = pf_encap_read (); + if (!emsg) + goto cleanup; + + *sz = sizeof emsg->em_gen_spi; + spi = malloc (*sz); + if (!spi) + goto cleanup; + memcpy (spi, &emsg->em_gen_spi, *sz); + free (emsg); + + log_debug_buf (LOG_MISC, 50, "pf_encap_get_spi: spi", spi, *sz); + + return spi; + + cleanup: + if (emsg) + free (emsg); + if (spi) + free (spi); + return 0; +} + +/* Group 2 SPIs in a chain. XXX not implemented yet. */ +int +pf_encap_group_spis (struct sa *sa, struct proto *proto1, struct proto *proto2, + int role) +{ + struct encap_msghdr *emsg = 0; + struct sockaddr *dst; + int dstlen; + + emsg = calloc (1, EMT_GRPSPIS_FLEN); + if (!emsg) + return -1; + + emsg->em_msglen = EMT_GRPSPIS_FLEN; + emsg->em_type = EMT_GRPSPIS; + + memcpy (&emsg->em_rel_spi, proto1->spi[role], sizeof emsg->em_rel_spi); + memcpy (&emsg->em_rel_spi2, proto2->spi[role], + sizeof emsg->em_rel_spi2); + sa->transport->vtbl->get_dst (sa->transport, &dst, &dstlen); + emsg->em_rel_dst = emsg->em_rel_dst2 = ((struct sockaddr_in *)dst)->sin_addr; + /* XXX What if IPCOMP etc. comes along? */ + emsg->em_rel_sproto + = proto1->proto == IPSEC_PROTO_IPSEC_ESP ? IPPROTO_ESP : IPPROTO_AH; + emsg->em_rel_sproto2 + = proto2->proto == IPSEC_PROTO_IPSEC_ESP ? IPPROTO_ESP : IPPROTO_AH; + + if (pf_encap_write (emsg)) + goto cleanup; + free (emsg); + + log_debug (LOG_MISC, 50, "pf_encap_group_spis: done"); + + return 0; + + cleanup: + if (emsg) + free (emsg); + return -1; +} + +/* Store/update a SPI with full information into the kernel. */ +int +pf_encap_set_spi (struct sa *sa, struct proto *proto, int role, int initiator) +{ + struct encap_msghdr *emsg = 0; + struct ipsec_proto *iproto = proto->data; + struct sockaddr *dst, *src; + int dstlen, srclen, keylen, hashlen; + size_t len; + struct esp_new_xencap *edx; + struct ah_new_xencap *amx; + + switch (proto->proto) + { + case IPSEC_PROTO_IPSEC_ESP: + keylen = ipsec_esp_enckeylength (proto); + hashlen = ipsec_esp_authkeylength (proto); + len = EMT_SETSPI_FLEN + ESP_NEW_XENCAP_LEN + keylen + hashlen + 8; + emsg = calloc (1, len); + if (!emsg) + return -1; + + /* Whenever should the "old" transforms be used? Policy thing? */ + emsg->em_alg = XF_NEW_ESP; + emsg->em_sproto = IPPROTO_ESP; + + edx = (struct esp_new_xencap *)emsg->em_dat; + + switch (proto->id) + { + case IPSEC_ESP_DES: + case IPSEC_ESP_DES_IV32: + case IPSEC_ESP_DES_IV64: + edx->edx_enc_algorithm = ALG_ENC_DES; + break; + + case IPSEC_ESP_3DES: + edx->edx_enc_algorithm = ALG_ENC_3DES; + break; + + case IPSEC_ESP_CAST: + edx->edx_enc_algorithm = ALG_ENC_CAST; + break; + + case IPSEC_ESP_BLOWFISH: + edx->edx_enc_algorithm = ALG_ENC_BLF; + break; + + default: + /* XXX Log? */ + return -1; + } + + switch (iproto->auth) + { + case IPSEC_AUTH_HMAC_MD5: + edx->edx_hash_algorithm = ALG_AUTH_MD5; + break; + + case IPSEC_AUTH_HMAC_SHA: + edx->edx_hash_algorithm = ALG_AUTH_SHA1; + break; + + case IPSEC_AUTH_DES_MAC: + case IPSEC_AUTH_KPDK: + /* XXX Log? */ + return -1; + + default: + edx->edx_hash_algorithm = 0; + } + + /* XXX What if we have a protocol requiring IV? */ + edx->edx_ivlen = 8; + edx->edx_confkeylen = keylen; + edx->edx_authkeylen = hashlen; + edx->edx_wnd = 16; + edx->edx_flags = iproto->auth ? ESP_NEW_FLAG_AUTH : 0; + memcpy (edx->edx_data + 8, iproto->keymat[role], keylen); + if (iproto->auth) + memcpy (edx->edx_data + keylen + 8, iproto->keymat[role] + keylen, + hashlen); + break; + + case IPSEC_PROTO_IPSEC_AH: + hashlen = ipsec_ah_keylength (proto); + len = EMT_SETSPI_FLEN + AH_NEW_XENCAP_LEN + hashlen; + emsg = calloc (1, len); + if (!emsg) + return -1; + + /* Whenever should the "old" transforms be used? Policy thing? */ + emsg->em_alg = XF_NEW_AH; + emsg->em_sproto = IPPROTO_AH; + + amx = (struct ah_new_xencap *)emsg->em_dat; + + switch (proto->id) + { + case IPSEC_AH_MD5: + amx->amx_hash_algorithm = ALG_AUTH_MD5; + break; + + case IPSEC_AH_SHA: + amx->amx_hash_algorithm = ALG_AUTH_SHA1; + break; + + default: + /* XXX Log? */ + goto cleanup; + } + + amx->amx_keylen = hashlen; + amx->amx_wnd = 16; + memcpy (amx->amx_key, iproto->keymat[role], hashlen); + break; + + default: + /* XXX Log? */ + goto cleanup; + } + + emsg->em_msglen = len; + emsg->em_type = EMT_SETSPI; + memcpy (&emsg->em_spi, proto->spi[role], sizeof emsg->em_spi); + emsg->em_ttl = IP4_DEFAULT_TTL; + /* Fill in a well-defined value in this reserved field. */ + emsg->em_satype = 0; + + /* + * XXX Addresses has to be thought through. Assumes IPv4. + */ + sa->transport->vtbl->get_dst (sa->transport, &dst, &dstlen); + sa->transport->vtbl->get_src (sa->transport, &src, &srclen); + emsg->em_dst + = ((struct sockaddr_in *)((initiator ^ role) ? dst : src))->sin_addr; + emsg->em_src + = ((struct sockaddr_in *)((initiator ^ role) ? src : dst))->sin_addr; + if (iproto->encap_mode == IPSEC_ENCAP_TUNNEL) + { + emsg->em_odst = emsg->em_dst; + emsg->em_osrc = emsg->em_src; + } + + /* XXX I am not sure which one is best in security respect. */ +#if 0 + emsg->em_first_use_hard = (u_int64_t)sa->seconds; + /* XXX Perhaps we could calculate something out of the last negotiation. */ + emsg->em_first_use_soft = (u_int64_t)sa->seconds * 9 / 10; + emsg->em_expire_hard = 0; + emsg->em_expire_soft = 0; +#else + emsg->em_expire_hard = time ((time_t *)0) + (u_int64_t)sa->seconds; + /* XXX Perhaps we could calculate something out of the last negotiation. */ + emsg->em_expire_soft = time ((time_t *)0) + (u_int64_t)sa->seconds * 9 / 10; + emsg->em_first_use_hard = 0; + emsg->em_first_use_soft = 0; +#endif + emsg->em_bytes_hard = (u_int64_t)sa->kilobytes * 1024; + /* XXX A configurable ratio would be better. */ + emsg->em_bytes_soft = (u_int64_t)sa->kilobytes * 1024 * 9 / 10; + emsg->em_packets_hard = 0; + emsg->em_packets_soft = 0; + + log_debug (LOG_PF_ENCAP, 10, "pf_encap_set_spi: proto %d dst %s SPI 0x%x", + emsg->em_sproto, inet_ntoa (emsg->em_dst), htonl (emsg->em_spi)); + if (pf_encap_write (emsg)) + goto cleanup; + free (emsg); + + log_debug (LOG_PF_ENCAP, 50, "pf_encap_set_spi: done"); + + return 0; + + cleanup: + if (emsg) + free (emsg); + return -1; +} + +/* Delete a specific SPI from the IPSEC kernel subsystem. */ +int +pf_encap_delete_spi (struct sa *sa, struct proto *proto, int initiator) +{ + struct encap_msghdr *emsg = 0; + struct sockaddr *dst; + int dstlen; + + emsg = calloc (1, EMT_DELSPI_FLEN); + if (!emsg) + return -1; + + emsg->em_msglen = EMT_DELSPI_FLEN; + emsg->em_type = EMT_DELSPI; + + memcpy (&emsg->em_gen_spi, proto->spi[initiator], sizeof emsg->em_gen_spi); + sa->transport->vtbl->get_dst (sa->transport, &dst, &dstlen); + emsg->em_gen_dst = ((struct sockaddr_in *)dst)->sin_addr; + /* XXX What if IPCOMP etc. comes along? */ + emsg->em_gen_sproto + = proto->proto == IPSEC_PROTO_IPSEC_ESP ? IPPROTO_ESP : IPPROTO_AH; + + if (pf_encap_write (emsg)) + goto cleanup; + free (emsg); + + log_debug (LOG_MISC, 50, "pf_encap_delete_spi: done"); + + return 0; + + cleanup: + if (emsg) + free (emsg); + return -1; +} + +/* Enable a flow. */ +int +pf_encap_enable_spi (struct sa *sa, int initiator) +{ + struct encap_msghdr *emsg = 0; + struct sockaddr *dst, *src; + int dstlen, srclen; + struct proto *proto = TAILQ_FIRST (&sa->protos); + + emsg = calloc (1, EMT_ENABLESPI_FLEN); + if (!emsg) + return -1; + + emsg->em_msglen = EMT_ENABLESPI_FLEN; + emsg->em_type = EMT_ENABLESPI; + + sa->transport->vtbl->get_dst (sa->transport, &dst, &dstlen); + sa->transport->vtbl->get_src (sa->transport, &src, &srclen); + + memcpy (&emsg->em_ena_spi, proto->spi[!initiator], sizeof emsg->em_ena_spi); + emsg->em_ena_dst = ((struct sockaddr_in *)dst)->sin_addr; + + emsg->em_ena_isrc.s_addr = ((struct sockaddr_in *)src)->sin_addr.s_addr; + emsg->em_ena_ismask.s_addr = 0xffffffff; + emsg->em_ena_idst.s_addr = emsg->em_ena_dst.s_addr; + emsg->em_ena_idmask.s_addr = 0xffffffff; + /* XXX How to deduce if we need ENABLE_FLAG_LOCAL? */ + emsg->em_ena_flags = ENABLE_FLAG_REPLACE; + + /* XXX What if IPCOMP etc. comes along? */ + emsg->em_ena_sproto + = proto->proto == IPSEC_PROTO_IPSEC_ESP ? IPPROTO_ESP : IPPROTO_AH; + + if (pf_encap_write (emsg)) + goto cleanup; + + /* + * XXX The condition should be true if this machine is part of the source + * subnet. + */ + if (1) + { + /* + * This "route" is used for packets from this host where the source + * address has not yet been decided. + */ + emsg->em_ena_flags |= ENABLE_FLAG_LOCAL; + if (pf_encap_write (emsg)) + goto cleanup; + } + free (emsg); + + log_debug (LOG_MISC, 50, "pf_encap_enable_spi: done"); + + return 0; + + cleanup: + if (emsg) + free (emsg); + return -1; +} diff --git a/sbin/isakmpd/pf_encap.h b/sbin/isakmpd/pf_encap.h new file mode 100644 index 00000000000..7df50b06670 --- /dev/null +++ b/sbin/isakmpd/pf_encap.h @@ -0,0 +1,65 @@ +/* $Id: pf_encap.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _PF_ENCAP_H_ +#define _PF_ENCAP_H_ + +#include <sys/queue.h> + +struct proto; +struct sa; + +struct pf_encap_node { + /* Link to next node. */ + TAILQ_ENTRY (pf_encap_node) link; + + /* The message itself. */ + struct encap_msghdr *emsg; + + /* The callback function and its argument. */ + void (*callback) (void *); + void *arg; +}; + +extern int pf_encap_delete_spi (struct sa *, struct proto *, int); +extern int pf_encap_enable_spi (struct sa *, int); +extern u_int8_t *pf_encap_get_spi (size_t *, u_int8_t, void *, size_t); +extern int pf_encap_group_spis (struct sa *, struct proto *, struct proto *, + int); +extern void pf_encap_handler (int); +extern int pf_encap_open (void); +extern int pf_encap_set_spi (struct sa *, struct proto *, int, int); + +#endif /* _PF_ENCAP_H_ */ diff --git a/sbin/isakmpd/pkcs.h b/sbin/isakmpd/pkcs.h new file mode 100644 index 00000000000..88ba975f79a --- /dev/null +++ b/sbin/isakmpd/pkcs.h @@ -0,0 +1,79 @@ +/* $Id: pkcs.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _PKCS_H_ +#define _PKCS_H_ + +#include <gmp.h> + +#define PKCS_PRIVATE 1 /* Private Key Encryption */ +#define PKCS_PUBLIC 2 /* Public Key Encryption */ + +struct rsa_public_key { + mpz_t n; /* Group Modulus */ + mpz_t e; /* Public Exponent */ +}; + +struct rsa_private_key { + mpz_t n; /* Group Modulus */ + mpz_t p; /* Prime p */ + mpz_t q; /* Prime q */ + mpz_t e; /* Public Exponent */ + mpz_t d; /* Private Exponent */ +}; + +struct norm_type; + +int pkcs_mpz_to_norm_type (struct norm_type *obj, mpz_ptr n); + +u_int8_t *pkcs_public_key_to_asn (struct rsa_public_key *); +int pkcs_public_key_from_asn (struct rsa_public_key *, u_int8_t *, u_int32_t); +void pkcs_free_public_key (struct rsa_public_key *); + +u_int8_t *pkcs_private_key_to_asn (struct rsa_private_key *); +int pkcs_private_key_from_asn (struct rsa_private_key *, u_int8_t *, + u_int32_t); +void pkcs_free_private_key (struct rsa_private_key *); + +int pkcs_rsa_encrypt (int, mpz_ptr, mpz_ptr, u_int8_t *, u_int32_t, + u_int8_t **, u_int32_t *); +int pkcs_rsa_decrypt (int, mpz_ptr, mpz_ptr, u_int8_t *, u_int8_t **, + u_int16_t *); + +int pkcs_generate_rsa_keypair (struct rsa_public_key *, + struct rsa_private_key *, u_int32_t); +int pkcs_generate_prime (mpz_ptr, u_int32_t); + +#endif /* _PKCS_H_ */ diff --git a/sbin/isakmpd/prf.c b/sbin/isakmpd/prf.c new file mode 100644 index 00000000000..b42911963f9 --- /dev/null +++ b/sbin/isakmpd/prf.c @@ -0,0 +1,165 @@ +/* $Id: prf.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <stdlib.h> +#include <string.h> + +#include "hash.h" +#include "log.h" +#include "prf.h" + +void prf_hash_init (struct prf_hash_ctx *); +void prf_hash_update (struct prf_hash_ctx *, unsigned char *, unsigned int); +void prf_hash_final (unsigned char *, struct prf_hash_ctx *); + +/* PRF behaves likes a hash */ + +void +prf_hash_init (struct prf_hash_ctx *ctx) +{ + memcpy (ctx->hash->ctx, ctx->ctx, ctx->hash->ctxsize); + memcpy (ctx->hash->ctx2, ctx->ctx2, ctx->hash->ctxsize); +} + +void +prf_hash_update (struct prf_hash_ctx *ctx, unsigned char *data, + unsigned int len) +{ + ctx->hash->Update (ctx->hash->ctx, data, len); +} + +void +prf_hash_final (unsigned char *digest, struct prf_hash_ctx *ctx) +{ + ctx->hash->HMACFinal (digest, ctx->hash); +} + +/* + * Obtain a Pseudo-Random Function for us. At the moment this is + * the HMAC version of a hash. See RFC-2104 for reference. + */ + +struct prf * +prf_alloc (enum prfs type, int subtype, char *shared, int sharedsize) +{ + struct hash *hash; + struct prf *prf; + + switch (type) + { + case PRF_HMAC: + hash = hash_get (subtype); + if (hash == NULL) + return NULL; + break; + default: + log_print ("Unkown PRF type %d in prf_alloc()", type); + return NULL; + } + + if ((prf = malloc (sizeof (struct prf))) == NULL) + { + log_print ("Out of memory for struct prf in prf_alloc()"); + return NULL; + } + + if (type == PRF_HMAC) + { + struct prf_hash_ctx *prfctx; + + /* Obtain needed memory */ + prfctx = malloc (sizeof (struct prf_hash_ctx)); + if (prfctx == NULL) + { + log_print ("Out of memory for struct prf_hash_ctx in prf_alloc()"); + goto cleanprf; + } + prf->prfctx = prfctx; + + prfctx->ctx = malloc (hash->ctxsize); + if (prfctx->ctx == NULL) + { + log_print ("Out of memory for ctx in prf_alloc()"); + goto cleanprfctx; + } + + prfctx->ctx2 = malloc (hash->ctxsize); + if (prfctx->ctx2 == NULL) + { + log_print ("Out of memory for ctx2 in prf_alloc()"); + free (prfctx->ctx); + goto cleanprfctx; + } + prf->type = PRF_HMAC; + prf->blocksize = hash->hashsize; + prfctx->hash = hash; + + /* Use the correct function pointers */ + prf->Init = (void (*) (void *)) prf_hash_init; + prf->Update + = (void (*) (void *, unsigned char *, unsigned int)) prf_hash_update; + prf->Final = (void (*) (unsigned char *, void *)) prf_hash_final; + + /* Init HMAC contexts */ + hash->HMACInit (hash, shared, sharedsize); + + /* Save contexts */ + memcpy (prfctx->ctx, hash->ctx, hash->ctxsize); + memcpy (prfctx->ctx2, hash->ctx2, hash->ctxsize); + } + + return prf; + + cleanprfctx: + free (prf->prfctx); + cleanprf: + free (prf); + return NULL; +} + + +void +prf_free (struct prf *prf) +{ + if (prf->type == PRF_HMAC) + { + struct prf_hash_ctx *prfctx = prf->prfctx; + free (prfctx->ctx2); + free (prfctx->ctx); + } + free (prf->prfctx); + free (prf); +} diff --git a/sbin/isakmpd/prf.h b/sbin/isakmpd/prf.h new file mode 100644 index 00000000000..d6a9023a5b2 --- /dev/null +++ b/sbin/isakmpd/prf.h @@ -0,0 +1,61 @@ +/* $Id: prf.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _PRF_H_ +#define _PRF_H_ + +/* Enumeration of possible PRF - Pseudo-Random Functions. */ +enum prfs { + PRF_HMAC = 0, /* No PRFs in drafts, this is the default */ +}; + +struct prf { + enum prfs type; /* Type of PRF */ + void *prfctx; /* Context for PRF */ + u_int8_t blocksize; /* The blocksize of PRF */ + void (*Init) (void *); + void (*Update) (void *, unsigned char *, unsigned int); + void (*Final) (unsigned char *, void *); +}; + +struct prf_hash_ctx { + struct hash *hash; /* Hash type to use */ + void *ctx, *ctx2; /* Contexts we need for later */ +}; + +struct prf *prf_alloc (enum prfs, int, char *, int); +void prf_free (struct prf *); + +#endif /* _PRF_H_ */ diff --git a/sbin/isakmpd/regress/Makefile b/sbin/isakmpd/regress/Makefile new file mode 100644 index 00000000000..8b102ccee45 --- /dev/null +++ b/sbin/isakmpd/regress/Makefile @@ -0,0 +1,38 @@ +# $Id: Makefile,v 1.1 1998/11/15 00:03:49 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +SUBDIR= asn b2n crypto dh ec2n exchange group hmac pkcs prf rsakeygen x509 + +.include <bsd.subdir.mk> diff --git a/sbin/isakmpd/regress/asn/Makefile b/sbin/isakmpd/regress/asn/Makefile new file mode 100644 index 00000000000..62c5e1effaf --- /dev/null +++ b/sbin/isakmpd/regress/asn/Makefile @@ -0,0 +1,14 @@ +# Test ASN + +PROG= asntest +SRCS= asntest.c conf.c asn.c asn_useful.c gmp_util.c log.c pkcs.c \ + sysdep.c hash.c x509.c +TOPOBJ!= cd ${.CURDIR}/../..; printf "all:\n\t@pwd\n" |${MAKE} -f- +.PATH: ${.CURDIR}/../.. ${TOPOBJ} +LDADD+= -lgmp +DPADD+= ${LIBDES} +NOMAN= +CFLAGS+= -I${.CURDIR}/../.. -I${TOPOBJ} -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/asn/asntest.c b/sbin/isakmpd/regress/asn/asntest.c new file mode 100644 index 00000000000..2d254cfd764 --- /dev/null +++ b/sbin/isakmpd/regress/asn/asntest.c @@ -0,0 +1,147 @@ +/* $Id: asntest.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <stdio.h> +#include <string.h> +#include <gmp.h> + +#include "conf.h" +#include "asn.h" +#include "asn_useful.h" +#include "pkcs.h" +#include "x509.h" + +int +main (void) +{ + char buf[1000]; + char buf2[1000]; + u_int32_t len; + struct norm_type test = SEQ("test", Signed); + struct norm_type test2 = SEQ("cert", Certificate); + struct norm_type *tmp, *tmp2; + struct rsa_public_key key; + struct x509_certificate cert; + int i, j; + u_int8_t *asn; + char *p; + + FILE *f = fopen ("ssh-test-ca.pem", "r"); + len = 0; + while (conf_get_line (f, buf + len, sizeof (buf) - len)) + if (buf[len] != '-') + len = strlen (buf); + + conf_decode_base64 (buf, &len, buf); + + asn_template_clone (&test, 1); + + asn_decode_sequence (buf, len, &test); + + p = ASN_SIGNED_ALGORITHM(&test); + + printf ("ObjectId: %s = %s\n", p, asn_parse_objectid (asn_ids, p)); + + asn_template_clone (&test2, 1); + + len = asn_get_len (ASN_SIGNED_DATA(&test)); + asn_decode_sequence (ASN_SIGNED_DATA(&test), len, &test2); + + tmp = asn_decompose ("cert.version", &test2); + printf ("Version: "); mpz_out_str (stdout, 16, tmp->data); + tmp = asn_decompose ("cert.serialNumber", &test2); + printf ("\nSerialNumber: "); mpz_out_str (stdout, 16, tmp->data); + tmp = asn_decompose ("cert.signature.algorithm", &test2); + printf ("\nsignature: %s\n", + asn_parse_objectid (asn_ids, (char *)tmp->data)); + + tmp = ASN_CERT_VALIDITY(&test2); + printf ("Begin: %s, End: %s\n", ASN_VAL_BEGIN(tmp), ASN_VAL_END(tmp)); + + i = 0; + while (1) + { + sprintf (buf2, "cert.issuer.RelativeDistinguishedName[%d]", i++); + tmp = asn_decompose (buf2, &test2); + if (tmp == NULL) + break; + + j = 0; + while (1) + { + sprintf (buf2, "RelativeDistinguishedName.AttributeValueAssertion[%d].AttributeType", j); + tmp2 = asn_decompose (buf2, tmp); + if (tmp2 == NULL) + break; + + printf ("Issuer: (%s) ", + asn_parse_objectid (asn_ids, tmp2->data)); + sprintf (buf2, "RelativeDistinguishedName.AttributeValueAssertion[%d].AttributeValue", j++); + tmp2 = asn_decompose (buf2, tmp); + printf ("%s\n", (char *)tmp2->data); + } + }; + + tmp = asn_decompose ("cert.subjectPublicKeyInfo.algorithm.algorithm", &test2); + printf ("Key: %s\n", asn_parse_objectid (asn_ids, tmp->data)); + + tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &test2); + asn = tmp->data + 1; + + pkcs_public_key_from_asn (&key, asn, asn_get_len (asn)); + printf ("n (%u): 0x", (unsigned int)mpz_sizeinbase (key.n, 2)); + mpz_out_str (stdout, 16, key.n); + printf ("\ne: 0x"); mpz_out_str (stdout, 16, key.e); + printf ("\n"); + + printf ("Validate SIGNED: "); + if (!x509_validate_signed (buf, asn_get_len (buf), &key, &asn, &len)) + printf ("FAILED "); + else + printf ("OKAY "); + printf ("\n"); + + memset (&cert, 0, sizeof (cert)); + x509_decode_certificate (buf, asn_get_len (buf), &cert); + + printf ("Encoding Certificiate: "); + if (!x509_encode_certificate(&cert, &asn, &len)) + printf ("FAILED "); + else + printf ("OKAY "); + printf ("\n"); + return 1; +} diff --git a/sbin/isakmpd/regress/asn/ssh-test-ca.pem b/sbin/isakmpd/regress/asn/ssh-test-ca.pem new file mode 100644 index 00000000000..4721db3bedb --- /dev/null +++ b/sbin/isakmpd/regress/asn/ssh-test-ca.pem @@ -0,0 +1,12 @@ +-----BEGIN X509 CERTIFICATE----- +MIIB/DCCAWWgAwIBAgIDAeD0MA0GCSqGSIb3DQEBBAUAMDgxCzAJBgNVBAYTAkZJMSkwJwYDVQQ +KEyBTc2ggQ29tbXVuaWNhdGlvbnMgU2VjdXJpdHkgTHRkLjAeFw05NzEyMzEwMDAwMDBaFw05OD +EyMzEwMDAwMDBaMDgxCzAJBgNVBAYTAkZJMSkwJwYDVQQKEyBTc2ggQ29tbXVuaWNhdGlvbnMgU +2VjdXJpdHkgTHRkLjCBnTANBgkqhkiG9w0BAQEFAAOBiwAwgYcCgYEAmxrZfHh3PXzt4STZ27xN +v6ccHA5Zs2rJ/NmjTz+cDtJriGtfroPjPuI82H7QifrGAmG9+iHLP9bZKvs8Bur5avXTQmxg2kT +/53K74Tiox2hJEPWKNAPWKf8Y/sCXKJF0TEYtFlFCzkm+lmBmtuSDixgD5Xa1DNl3Ket7m4vOhq +8CASmjFjAUMBIGA1UdEwEB/wQIMAYBAf8CAQowDQYJKoZIhvcNAQEEBQADgYEAP66aK4rdFAT/H +PKGTEM1UQgmo8b/fi7rB90jonodOI4Xros/3R1Nj8Z5zQcx2hG5xjIAl9YpHmmPSbgtYD1SIFxF +0sWBa12FU7u/Sa8OjBvs9K0Ofnw/Sdp7on0M6f/xTVHKFCunfAbHsqFhieej6esDJBKODpbb2pJ +c6VphrlE= +-----END X509 CERTIFICATE----- diff --git a/sbin/isakmpd/regress/b2n/Makefile b/sbin/isakmpd/regress/b2n/Makefile new file mode 100644 index 00000000000..b84f0240a60 --- /dev/null +++ b/sbin/isakmpd/regress/b2n/Makefile @@ -0,0 +1,12 @@ + +# Test HMAC: + +PROG= b2ntest +SRCS= math_2n.c b2ntest.c util.c sysdep.c +.PATH: ${.CURDIR}/../../ +NOMAN= +TOPOBJ!= cd ${.CURDIR}/../..; printf "all:\n\t@pwd\n" |${MAKE} -f- +CFLAGS+= -I${TOPOBJ} -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk>
\ No newline at end of file diff --git a/sbin/isakmpd/regress/b2n/b2ntest.c b/sbin/isakmpd/regress/b2n/b2ntest.c new file mode 100644 index 00000000000..94176384c0a --- /dev/null +++ b/sbin/isakmpd/regress/b2n/b2ntest.c @@ -0,0 +1,367 @@ +/* $Id: b2ntest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * B2N is a module for doing arithmetic on the Field GF(2**n) which is + * isomorph to ring of polynomials GF(2)[x]/p(x) where p(x) is an + * irreduciable polynomial over GF(2)[x] with grade n. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "math_2n.h" + +#define CMP_FAIL(n,x) b2n_sprint (buf, n); if (strcmp (buf, (x))) \ + printf ("FAILED: %s != %s ", buf, x); else printf ("OKAY "); + +int +main (void) +{ + int i; + b2n_t n, m, d, r; + char buf[200]; + + b2n_init (n); + b2n_init (m); + b2n_init (d); + b2n_init (r); + + printf ("Arithimetic Tests for GF(2)[x]:\n"); + printf ("Testing: b2n_set*: "); + b2n_set_ui (n, 0xffc0); + CMP_FAIL (n, "0xffc0"); + + b2n_set_str (m, "0x180c0"); + CMP_FAIL (m, "0x0180c0"); + b2n_set_str (m, "0x808b8080c0"); + CMP_FAIL (m, "0x808b8080c0"); + + printf ("\nTesting: b2n_add: "); + b2n_add (d, n, m); + CMP_FAIL (d, "0x808b807f00"); + b2n_add (n, n, m); + CMP_FAIL (n, "0x808b807f00"); + b2n_add (n, n, n); + CMP_FAIL (n, "0x00"); + b2n_set_str (n, "0x9090900000000000000000"); + b2n_set_ui (m, 0); + b2n_add (n, n, m); + CMP_FAIL (n, "0x9090900000000000000000"); + + printf ("\nTesting: b2n_lshift: "); + b2n_set_str (m, "0x808b8080c0"); + b2n_lshift (n, m, 3); + CMP_FAIL (n, "0x04045c040600"); + b2n_lshift (n, m, 11); + CMP_FAIL (n, "0x04045c04060000"); + b2n_set (n, m); + for (i = 0; i < 11; i++) + b2n_lshift (n, n, 1); + CMP_FAIL (n, "0x04045c04060000"); + b2n_lshift (d, m, 12); + CMP_FAIL (d, "0x0808b8080c0000"); + b2n_set_str (m, "0xdeadbeef"); + b2n_lshift (d, m, 103); + CMP_FAIL (d, "0x6f56df7780000000000000000000000000"); + + printf ("\nTesting: b2n_rshift: "); + b2n_rshift (m, n, 3); + CMP_FAIL (m, "0x808b8080c000"); + b2n_rshift (m, m, 11); + CMP_FAIL (m, "0x1011701018"); + b2n_set_str (m, "0x12381998713258186712365"); + b2n_rshift (m, m, 23); + CMP_FAIL (m, "0x024703330e264b030c"); + b2n_set_str (m, "0x12381998713258186712365"); + for (i=0; i<23; i++) + b2n_rshift (m, m, 1); + CMP_FAIL (m, "0x024703330e264b030c"); + + printf ("\nTesting: b2n_mul: 0x9 o 0x5: "); + b2n_set_ui (n, 9); + b2n_set_ui (m, 5); + b2n_mul (d, n, m); + CMP_FAIL (d, "0x2d"); + b2n_mul (n, n, m); + CMP_FAIL (d, "0x2d"); + + printf ("\nTesting: b2n_mul: 0x9 o 0x0: "); + b2n_set_ui (n, 9); + b2n_set_ui (m, 0); + b2n_mul (d, n, m); + CMP_FAIL (d, "0x00"); + b2n_set_ui (n, 0); + b2n_set_ui (m, 9); + b2n_mul (d, n, m); + CMP_FAIL (d, "0x00"); + + printf ("\nTesting: b2n_mul: 0x9 o 0x1: "); + b2n_set_ui (n, 9); + b2n_set_ui (m, 1); + b2n_mul (d, n, m); + CMP_FAIL (d, "0x09"); + + printf ("\nTesting: b2n_mul: 0x12329 o 0x1235: "); + b2n_set_str (n, "0x12329"); + b2n_set_str (m, "0x1235"); + b2n_mul (d, n, m); + CMP_FAIL (d, "0x10473a3d"); + b2n_mul (n, n, m); + CMP_FAIL (d, "0x10473a3d"); + + printf ("\nTesting: b2n_square: 0x1235 o 0x1235: "); + b2n_set_str (m, "0x1235"); + b2n_square (n, m); + CMP_FAIL (n, "0x01040511"); + + printf ("\nTesting: b2n_square: 0x80c1235 o 0x80c1235: "); + b2n_set_str (m, "0x80c1235"); + b2n_square (n, m); + CMP_FAIL (n, "0x40005001040511"); + + b2n_set_str (m, "0x12329"); + printf ("\nTesting: sigbit: 0x12329: %d, %s", + b2n_sigbit(m), b2n_sigbit(m) == 17 ? "OKAY" : "FAILED"); + b2n_set_ui (m, 0); + printf ("\nTesting: sigbit: 0x0: %d, %s", + b2n_sigbit(m), b2n_sigbit(m) == 0 ? "OKAY" : "FAILED"); + b2n_set_str (m, "0x7f3290000"); + printf ("\nTesting: sigbit: 0x7f3290000: %d, %s", + b2n_sigbit(m), b2n_sigbit(m) == 35 ? "OKAY" : "FAILED"); + + printf ("\nTesting: b2n_cmp: "); + b2n_set_str (m, "0x2234"); + b2n_set_str (n, "0x1234"); + printf ("%d <-> %d, ", b2n_sigbit (m), b2n_sigbit(n)); + printf ("%d, %d ,%d: ", b2n_cmp (m,m), b2n_cmp (m,n), b2n_cmp (n,m)); + if (b2n_cmp (m,m) || b2n_cmp (m,n) != 1 || b2n_cmp (n,m) != -1) + printf ("FAILED"); + else + printf ("OKAY"); + printf ("\nTesting: b2n_cmp_null: "); + b2n_set_str (m, "0x2234"); + b2n_set_ui (n, 0); + printf ("%d, %d: ", b2n_cmp_null (m), b2n_cmp_null (n)); + if (b2n_cmp_null (m) != 1 || b2n_cmp_null (n)) + printf ("FAILED"); + else + printf ("OKAY"); + + printf ("\nTesting: b2n_div: 0x2d / 0x5: "); + b2n_set_str (n, "0x2d"); + b2n_set_ui (m, 5); + b2n_div (n, m, n, m); + CMP_FAIL (n, "0x09"); + CMP_FAIL (m, "0x00"); + printf ("\nTesting: b2n_div: 0x2d / 0x1: "); + b2n_set_str (n, "0x2d"); + b2n_set_ui (m, 1); + b2n_div (n, m, n, m); + CMP_FAIL (n, "0x2d"); + CMP_FAIL (m, "0x00"); + + printf ("\nTesting: b2n_div: 0x10473a3d / 0x1235: "); + b2n_set_str (n, "0x10473a3d"); + b2n_set_str (m, "0x1235"); + b2n_div (n, m, n, m); + CMP_FAIL (n, "0x012329"); + CMP_FAIL (m, "0x00"); + + printf ("\nTesting: b2n_div: 0x10473a3d / 0x1536: "); + b2n_set_str (n, "0x10473a3d"); + b2n_set_str (m, "0x1536"); + b2n_div (n, m, n, m); + CMP_FAIL (n, "0x014331"); + CMP_FAIL (m, "0xab"); + b2n_set_str (n, "0x10473a3d"); + b2n_set_str (m, "0x1536"); + b2n_div_q (d, n, m); + CMP_FAIL (d, "0x014331"); + b2n_div_r (d, n, m); + CMP_FAIL (d, "0xab"); + + printf ("\nTesting: b2n_div: 0x0800000000000000000000004000000000000001 / 0xffab09909a00: "); + b2n_set_str (n, "0x0800000000000000000000004000000000000001"); + b2n_set_str (m, "0xffab09909a00"); + b2n_div_q (d, n, m); + CMP_FAIL (d, "0x18083e83a98647cedae0b3e69a5e"); + b2n_div_r (d, n, m); + CMP_FAIL (d, "0x5b8bf98cac01"); + b2n_set (d, m); + b2n_div (n, m, n, m); + CMP_FAIL (n, "0x18083e83a98647cedae0b3e69a5e"); + CMP_FAIL (m, "0x5b8bf98cac01"); + + printf ("\nTesting: b2n_div: 0x0800000000000000000000004000000000000001 / 0x7b: "); + b2n_set_str (n, "0x0800000000000000000000004000000000000001"); + b2n_set_str (m, "0x7b"); + b2n_div (n, m, n, m); + CMP_FAIL (n, "0x32dea27065bd44e0cb7a89c000000000000000"); + CMP_FAIL (m, "0x01"); + + printf ("\n\nArithimetic Tests for GF(2**m) ~= GF(2)[x]/p(x):\n"); + printf ("Testing: b2n_gcd: "); + b2n_set_str (d, "0x771"); + b2n_set_str (m, "0x26d"); + b2n_gcd (n, m, d); + CMP_FAIL (n, "0x0b"); + b2n_set_str (d, "0x0800000000000000000000004000000000000001"); + b2n_set_str (m, "0xffab09909a00"); + b2n_gcd (n, m, d); + CMP_FAIL (n, "0x01"); + b2n_set_str (d, "0x0800000000000000000000004000000000000001"); + b2n_set_str (m, "0x7b"); + b2n_gcd (n, m, d); + CMP_FAIL (n, "0x01"); + + printf ("\nTesting: b2n_mul_inv: "); + b2n_set_str (d, "0x0800000000000000000000004000000000000001"); + b2n_set_str (m, "0xffab09909a00"); + b2n_mul_inv (n, m, d); + CMP_FAIL (n, "0x074029149f69304174d28858ae5c60df208a22a8"); + b2n_set_str (n, "0xffab09909a00"); + b2n_mul_inv (n, n, d); + CMP_FAIL (n, "0x074029149f69304174d28858ae5c60df208a22a8"); + b2n_mul (n, n, m); + b2n_mod (n, n, d); + CMP_FAIL (n, "0x01"); + b2n_set_str (d, "0x0800000000000000000000004000000000000001"); + b2n_set_str (m, "0x7b"); + b2n_mul_inv (n, m, d); + CMP_FAIL (n, "0x32dea27065bd44e0cb7a89c000000000000000"); + b2n_mul (n, n, m); + b2n_mod (n, n, d); + CMP_FAIL (n, "0x01"); + + printf ("\nTesting: b2n_random: "); + b2n_random (m, 155); + b2n_sprint (buf, m); + printf ("%s, %d", buf, b2n_sigbit(m)); + + printf ("\nTesting: b2n_sqrt: "); + b2n_set_str (n, "0x0800000000000000000000004000000000000001"); + b2n_set_ui (d, 2); + b2n_sqrt (m, d, n); + b2n_square (d, m); + b2n_add (d, d, m); + b2n_mod (d, d, n); + CMP_FAIL (d, "0x02"); + + /* x**3 + b */ + b2n_set_ui (n, 0x7b); + b2n_square (d, n); + b2n_mul (d, d, n); + b2n_set_str (n, "0x07338f"); + b2n_add (d, d, n); + b2n_set_str (n, "0x0800000000000000000000004000000000000001"); + b2n_mod (d, d, n); + /* \alpha = x**3 + b - end */ + + /* \beta = x**(-2)*\alpha */ + b2n_set_ui (m, 0x7b); + b2n_mul_inv (m, m, n); + b2n_square (m, m); + b2n_mod (m, m, n); + b2n_mul (d, d, m); + b2n_mod (d, d, n); + b2n_set (r, d); + /* \beta = x**(-2)*\alpha - end */ + + b2n_sqrt (m, d, n); + CMP_FAIL (m, "0x0690aec7cd215d8f9a42bb1f0000000000000004"); + b2n_square (d, m); + b2n_mod (d, d, n); + b2n_add (d, d, m); + b2n_mod (d, d, n); + printf ("Squaring Check: "); + CMP_FAIL (d, "0x03d5af92c8311d9e8f56be4b3e690aec7cd215cc"); + + printf ("\nTesting: b2n_trace: "); + b2n_set_ui (m, 2); + b2n_trace (d, m, n); + CMP_FAIL (d, "0x00"); + b2n_set_ui (m, 0x11223); + b2n_trace (d, m, n); + CMP_FAIL (d, "0x01"); + + printf ("\nTesting: b2n_exp_mod: "); + b2n_set_ui (m, 0x7b); + b2n_exp_mod (d, m, 5, n); + CMP_FAIL (d, "0x7cccb7cb"); + b2n_set_str (m, "0x123456789abcdef"); + b2n_exp_mod (d, m, 13, n); + CMP_FAIL (d, "0x043f0a8550cb69b3c50d0340d1c6d5c97ecd60d4"); + + printf ("\nTesting: b2n_3mul: "); + b2n_set_ui (m, 0x7b); + b2n_3mul (m, m); + CMP_FAIL (m, "0x0171"); + + b2n_set_ui (m, 0x7fffffff); + b2n_3mul (m, m); + CMP_FAIL (m, "0x017ffffffd"); + + printf ("\nTesting: b2n_nadd: "); + b2n_set_str (m, "0x7fffffff"); + b2n_set_str (n, "0x10203045"); + b2n_nadd (d, n, m); + CMP_FAIL (d, "0x90203044"); + + b2n_set_str (m, "0x9a4a54d8b8dfa566112849991214329a233d"); + b2n_set_str (n, "0x70ee40dd60c8657e58eda9a17ad9176e28b4b457e5a34a0948e335"); + b2n_nadd (d, n, m); + CMP_FAIL (d, "0x70ee40dd60c8657e5987f3f65391f7138ec5dca17eb55e3be30672"); + + printf ("\nTesting: b2n_nsub: "); + b2n_set_str (n, "0x90203044"); + b2n_set_str (m, "0x10203045"); + b2n_nsub (d, n, m); + CMP_FAIL (d, "0x7fffffff"); + + b2n_set_str (n, "0x70ee40dd60c8657e5987f3f65391f7138ec5dca17eb55e3be30672"); + b2n_set_str (m, "0x70ee40dd60c8657e58eda9a17ad9176e28b4b457e5a34a0948e335"); + b2n_nsub (d, n, m); + CMP_FAIL (d, "0x9a4a54d8b8dfa566112849991214329a233d"); + + b2n_clear (n); + b2n_clear (m); + b2n_clear (d); + b2n_clear (r); + + printf ("\n"); + return 1; +} diff --git a/sbin/isakmpd/regress/check.sh b/sbin/isakmpd/regress/check.sh new file mode 100644 index 00000000000..f5d1d128cad --- /dev/null +++ b/sbin/isakmpd/regress/check.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# $Id: check.sh,v 1.1 1998/11/15 00:03:49 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Niklas Hallqvist. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson. +# + +PROGNAME=$0 +NC=/usr/bin/nc +HOST=localhost +ISAKMP_PORT=500 + +set -- `getopt p: $*` +if [ $? != 0 ]; then + echo 'usage: $PROGNAME [-p port] host' >&2 + exit 2 +fi +for i; do + case "$i" in + -p) + ISAKMP_PORT=$2; shift; shift;; + --) + shift; break;; + esac +done + +if [ $# -gt 0 ]; then + HOST=$1 +fi + +send () { + ${NC} -u -w 1 ${HOST} ${ISAKMP_PORT} +} + +# Short message +printf "SHORT!" |send + +# (Most probably) invalid cookie +printf "INVALID COOKIES!\0\x10\0\0\0\0\0\0\0\0\0\x1c" |send + +# Invalid next payload type +printf "01234567\0\0\0\0\0\0\0\0!\x10\0\0\0\0\0\0\0\0\0\x1c" |send + +# Invalid major version +printf "01234567\0\0\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\0\0\x1c" |send + +# Invalid minor version +printf "01234567\0\0\0\0\0\0\0\0\0\x11\0\0\0\0\0\0\0\0\0\x1c" |send + +# Invalid exchange type +printf "01234567\0\0\0\0\0\0\0\0\0\x10!\0\0\0\0\0\0\0\0\x1c" |send + +# Invalid flags +printf "01234567\0\0\0\0\0\0\0\0\0\x10\2\x80\0\0\0\0\0\0\0\x1c" |send + +# Invalid message ID +printf "01234567\0\0\0\0\0\0\0\0\0\x10\2\0BAD!\0\0\0\x1c" |send + +# Short length +printf "01234567\0\0\0\0\0\0\0\0\0\x10\2\0\0\0\0\0\0\0\0\x1b" |send + +# Long length +printf "01234567\0\0\0\0\0\0\0\0\0\x10\2\0\0\0\0\0\0\0\0\x1d" |send diff --git a/sbin/isakmpd/regress/crypto/Makefile b/sbin/isakmpd/regress/crypto/Makefile new file mode 100644 index 00000000000..26ed4656086 --- /dev/null +++ b/sbin/isakmpd/regress/crypto/Makefile @@ -0,0 +1,12 @@ +# Test Crypto: + +PROG= cryptotest +SRCS= log.c crypto.c cryptotest.c +.PATH: ${.CURDIR}/../../ +LDADD+= -ldes +DPADD+= ${LIBDES} +NOMAN= +CFLAGS+= -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/crypto/cryptotest.c b/sbin/isakmpd/regress/crypto/cryptotest.c new file mode 100644 index 00000000000..05aaa533912 --- /dev/null +++ b/sbin/isakmpd/regress/crypto/cryptotest.c @@ -0,0 +1,167 @@ +/* $Id: cryptotest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "crypto.h" + +void test_crypto (enum transform); + +#define SET_KEY(x,y) {int i; for (i=0; i < (y); i++) (x)[i] = i;} + +int +verify_buf (u_int8_t *buf, u_int16_t len) +{ + int i; + + for (i = 0; i < len; i++) + if (buf[i] != i) + return 0; + + return 1; +} + +#define nibble2bin(y) (tolower((y)) < 'a' ? (y) - '0': tolower((y)) - 'a' + 10) +#define hexchar2bin(x) ((nibble2bin((x)[0]) << 4) + nibble2bin((x)[1])) +#define nibble2c(x) ((x) >= 10 ? ('a'-10+(x)) : ('0' + (x))) + +void asc2bin (u_int8_t *bin, u_int8_t *asc, u_int16_t len) +{ + int i; + + for (i = 0; i < len; i += 2, asc += 2) + { + *bin++ = hexchar2bin(asc); + } +} + +void +special_test_blf (void) +{ + u_int8_t *akey = "0123456789ABCDEFF0E1D2C3B4A59687"; + u_int8_t *aiv = "FEDCBA9876543210"; + u_int8_t data[] = "7654321 Now is the time for \0\0\0"; /* len 29 */ + u_int8_t *acipher = "6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CCE7"; + u_int8_t key[16], cipher[32], iv[8]; + struct crypto_xf *xf; + struct keystate *ks; + enum cryptoerr err; + int i; + + asc2bin (key, akey, strlen (akey)); + asc2bin (iv, aiv, strlen (aiv)); + asc2bin (cipher, acipher, 64); + + xf = crypto_get (BLOWFISH_CBC); + printf ("Special Test-Case %s: ", xf->name); + + ks = crypto_init (xf, key, 16, &err); + if (!ks) + { + printf ("FAILED (init %d)", err); + goto fail; + } + + crypto_init_iv (ks, iv, xf->blocksize); + crypto_encrypt (ks, data, 32); + + for (i = 0; i < 32; i++) + if (data[i] != cipher[i]) + break; + if (i < 32) + printf ("FAILED "); + else + printf ("OKAY "); + + free (ks); + +fail: + printf ("\n"); + return; +} + +int +main (void) +{ + test_crypto (DES_CBC); + + test_crypto (TRIPLEDES_CBC); + + test_crypto (BLOWFISH_CBC); + + test_crypto (CAST_CBC); + + special_test_blf (); + + return 1; +} + +void +test_crypto (enum transform which) +{ + u_int8_t buf[256]; + struct crypto_xf *xf; + struct keystate *ks; + enum cryptoerr err; + + xf = crypto_get (which); + printf ("Testing %s: ", xf->name); + + SET_KEY (buf, xf->keymax); + ks = crypto_init (xf, buf, xf->keymax, &err); + if (!ks) + { + printf ("FAILED (init %d)", err); + goto fail; + } + SET_KEY (buf, sizeof (buf)); + crypto_init_iv (ks, buf, xf->blocksize); + crypto_encrypt (ks, buf, sizeof (buf)); + crypto_decrypt (ks, buf, sizeof (buf)); + if (!verify_buf (buf, sizeof (buf))) + printf ("FAILED "); + else + printf ("OKAY "); + + free (ks); + + fail: + printf ("\n"); + return; +} diff --git a/sbin/isakmpd/regress/dh/Makefile b/sbin/isakmpd/regress/dh/Makefile new file mode 100644 index 00000000000..39a873f4cdb --- /dev/null +++ b/sbin/isakmpd/regress/dh/Makefile @@ -0,0 +1,14 @@ +# Test DH: + +PROG= dhtest +SRCS= math_2n.c math_ec2n.c math_group.c dh.c dhtest.c util.c \ + log.c sysdep.c gmp_util.c +.PATH: ${.CURDIR}/../../ +NOMAN= +LDADD+= -lgmp +DPADD+= ${LIBGMP} +TOPOBJ!= cd ${.CURDIR}/../..; printf "all:\n\t@pwd\n" |${MAKE} -f- +CFLAGS+= -I${.CURDIR}/../../ -I${TOPOBJ} -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/dh/dhtest.c b/sbin/isakmpd/regress/dh/dhtest.c new file mode 100644 index 00000000000..cd9361177f9 --- /dev/null +++ b/sbin/isakmpd/regress/dh/dhtest.c @@ -0,0 +1,106 @@ +/* $Id: dhtest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * This module does a Diffie-Hellman Exchange + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "math_group.h" +#include "dh.h" + +#define DUMP_X(_x_) point = (_x_); b2n_print (point->x); + +int +main (void) +{ + int len; + char buf[100], buf2[100]; + char sec[100], sec2[100]; + struct group *group, *group2; + + group_init (); + group = group_get (4); + group2 = group_get (4); + + printf ("Testing DH (elliptic curve): \n"); + + printf ("dh_getlen\n"); + len = dh_getlen (group); + printf ("dh_create_exchange\n"); + dh_create_exchange (group, buf); + dh_create_exchange (group2, buf2); + + printf ("dh_create_shared\n"); + dh_create_shared (group, sec, buf2); + dh_create_shared (group2, sec2, buf); + + printf ("Result: "); + if (memcmp (sec, sec2, len)) + printf ("FAILED "); + else + printf ("OKAY "); + + group_free (group); + group_free (group2); + + printf ("\nTesting DH (MODP): \n"); + + group = group_get (1); + group2 = group_get (1); + + printf ("dh_getlen\n"); + len = dh_getlen (group); + printf ("dh_create_exchange\n"); + dh_create_exchange (group, buf); + dh_create_exchange (group2, buf2); + + printf ("dh_create_shared\n"); + dh_create_shared (group, sec, buf2); + dh_create_shared (group2, sec2, buf); + + printf ("Result: "); + if (memcmp (sec, sec2, len)) + printf ("FAILED "); + else + printf ("OKAY "); + + + printf ("\n"); + return 1; +} diff --git a/sbin/isakmpd/regress/ec2n/Makefile b/sbin/isakmpd/regress/ec2n/Makefile new file mode 100644 index 00000000000..9b51dd07aa5 --- /dev/null +++ b/sbin/isakmpd/regress/ec2n/Makefile @@ -0,0 +1,12 @@ + +# Test EC2N: + +PROG= ec2ntest +SRCS= math_2n.c math_ec2n.c ec2ntest.c util.c sysdep.c +.PATH: ${.CURDIR}/../../ +NOMAN= +TOPOBJ!= cd ${.CURDIR}/../..; printf "all:\n\t@pwd\n" |${MAKE} -f- +CFLAGS+= -I${TOPOBJ} -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/ec2n/ec2ntest.c b/sbin/isakmpd/regress/ec2n/ec2ntest.c new file mode 100644 index 00000000000..cbc5863f1f8 --- /dev/null +++ b/sbin/isakmpd/regress/ec2n/ec2ntest.c @@ -0,0 +1,146 @@ +/* $Id: ec2ntest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * B2N is a module for doing arithmetic on the Field GF(2**n) which is + * isomorph to ring of polynomials GF(2)[x]/p(x) where p(x) is an + * irreduciable polynomial over GF(2)[x] with grade n. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "math_2n.h" +#include "math_ec2n.h" + +#define CMP_FAIL(n,x) b2n_sprint (buf, n); if (strcmp (buf, (x))) \ + printf ("FAILED: %s != %s ", buf, x); else printf ("OKAY "); + +int +main (void) +{ + b2n_t k; + ec2np_t p, q, r; + ec2ng_t g; + char buf[200]; + + b2n_init (k); + ec2np_init (p); + ec2np_init (q); + ec2np_init (r); + ec2ng_init (g); + + printf ("Testing: ec2ng_set* :"); + /* Init Group */ + ec2ng_set_p_str (g, "0x0800000000000000000000004000000000000001"); + CMP_FAIL (g->p, "0x0800000000000000000000004000000000000001"); + ec2ng_set_a_ui (g, 0); + CMP_FAIL (g->a, "0x00"); + ec2ng_set_b_str (g, "0x07338f"); + CMP_FAIL (g->b, "0x07338f"); + + printf ("\nTesting: ec2np_find_y: "); + /* Init Point */ + ec2np_set_x_ui (p, 0x7b); + ec2np_find_y (p, g); + + CMP_FAIL (p->y, "0x01c8"); + + printf ("\nTesting: ec2np_ison: "); + if (ec2np_ison (p, g)) + printf ("OKAY "); + else + printf ("FAILED "); + + ec2np_set_x_ui (q, 0x4); + ec2np_find_y (q, g); + if (ec2np_ison (q, g)) + printf ("OKAY "); + else + printf ("FAILED "); + + printf ("\nTesting: ec2np_add: "); + ec2np_set (r, p); + b2n_add (r->y, r->y, r->x); + ec2np_add (r, r, p, g); + if (!r->inf) + printf ("FAILED "); + else + printf ("OKAY "); + + ec2np_add (q, p, q, g); + CMP_FAIL (q->x, "0x06f32d7cc82cec8612a87a86e026350fb7595469"); + CMP_FAIL (q->y, "0x4ab92e21e51358ca8deab3fbbc9f7d8a7d1575"); + if (ec2np_ison (q, g)) + printf ("OKAY "); + else + printf ("FAILED "); + + ec2np_add (p, q, q, g); + CMP_FAIL (p->x, "0x0390001461385559a22ac9b6181c1e1889b38451"); + CMP_FAIL (p->y, "0x0188e61f38d747d7813c6a8b33d14dfb7418b04c"); + if (ec2np_ison (p, g)) + printf ("OKAY "); + else + printf ("FAILED "); + + printf ("\nTesting: ec2np_mul: "); + b2n_set_ui (k, 57); + ec2np_set (q, p); + ec2np_mul (q, q, k, g); + if (ec2np_ison (q, g)) + printf ("OKAY "); + else + printf ("FAILED "); + CMP_FAIL (q->x, "0x06bcf88caab88f99399350c46559da3b91afbf9d"); + + b2n_set_str (k, "0x0800000000000000000057db5698537193aef943"); + ec2np_set (q, p); + ec2np_mul (q, q, k, g); + if (ec2np_ison (q, g)) + printf ("OKAY "); + else + printf ("FAILED "); + CMP_FAIL (q->x, "0x0390001461385559a22ac9b6181c1e1889b38451"); + + printf ("\n"); + ec2np_clear (p); + ec2np_clear (q); + ec2np_clear (r); + ec2ng_clear (g); + b2n_clear (k); + return 1; +} diff --git a/sbin/isakmpd/regress/exchange/Makefile b/sbin/isakmpd/regress/exchange/Makefile new file mode 100644 index 00000000000..b2e08338197 --- /dev/null +++ b/sbin/isakmpd/regress/exchange/Makefile @@ -0,0 +1,58 @@ +# $Id: Makefile,v 1.1 1998/11/15 00:03:50 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Ericsson Radio Systems. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson Radio Systems. +# + +TOPOBJ!= cd ${.CURDIR}/../..; printf "all:\n\t@pwd\n" |${MAKE} -f- +RUN= ISAKMPD=${TOPOBJ}/isakmpd ${.CURDIR}/run.sh + +TESTS= def + +all: + +test: ${TESTS:S/^/test-/} + +.for TEST in ${TESTS} +test-${TEST}: +.ifdef ONLY_INIT + @echo Testing "${TEST}" test as initiator + @${RUN} ${RUNFLAGS} ${.CURDIR}/${TEST} +.endif +.ifdef ONLY_RESP + @echo Testing "${TEST}" test as responder + @${RUN} -r ${RUNFLAGS} ${.CURDIR}/${TEST} +.endif +.endfor + +.include <bsd.obj.mk> +.include <bsd.subdir.mk> diff --git a/sbin/isakmpd/regress/exchange/def-i.1 b/sbin/isakmpd/regress/exchange/def-i.1 Binary files differnew file mode 100644 index 00000000000..17122493534 --- /dev/null +++ b/sbin/isakmpd/regress/exchange/def-i.1 diff --git a/sbin/isakmpd/regress/exchange/def-r.1 b/sbin/isakmpd/regress/exchange/def-r.1 Binary files differnew file mode 100644 index 00000000000..56f5e627c8e --- /dev/null +++ b/sbin/isakmpd/regress/exchange/def-r.1 diff --git a/sbin/isakmpd/regress/exchange/run.sh b/sbin/isakmpd/regress/exchange/run.sh new file mode 100644 index 00000000000..eb26c066c9d --- /dev/null +++ b/sbin/isakmpd/regress/exchange/run.sh @@ -0,0 +1,141 @@ +#!/bin/sh +# $Id: run.sh,v 1.1 1998/11/15 00:03:50 niklas Exp $ + +# +# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Niklas Hallqvist. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# This code was written under funding by Ericsson. +# + +# Defaults +SRCPORT=1500 +DSTPORT=1501 +FIFO=test.fifo +TIMEOUT=2 + +NC=${NC:-/usr/bin/nc} +ISAKMPD=${ISAKMPD:-/usr/sbin/isakmpd} + +progname=`basename $0` +indent=`echo -n $progname |sed 's/./ /g'` +seed=980801 +initiator=yes +retval=0 +verbose=no +clean=yes + +usage () +{ + echo "usage: $progname [-nrv] [-d dst-port] [-f fifo] [-s src-port]" >&2 + echo " $indent [-t timeout] testsuite" >&2 + exit 2 +} + +set -- `getopt d:f:nrs:t:v $*` +if [ $? != 0 ]; then + usage +fi +for i; do + case "$i" in + -d) + DSTPORT=$2; shift; shift;; + -f) + FIFO=$2; shift; shift;; + -n) + clean=no; shift;; + -r) + initiator=no; shift;; + -s) + SRCPORT=$2; shift; shift;; + -t) + TIMEOUT=$2; shift; shift;; + -v) + verbose=yes; shift;; + --) + shift; break;; + esac +done + +if [ $# -eq 1 ]; then + suite=$1 +else + usage +fi + +[ ${verbose} = yes ] && set -x + +# Start isakmpd and wait for the fifo to get created +rm -f ${FIFO} +${ISAKMPD} -d -p${SRCPORT} -f${FIFO} -r${seed} & +isakmpd_pid=$! +trap 'kill $isakmpd_pid; rm -f${FIFO}; [ $clean = yes ] && rm -f packet' 1 2 15 +while [ ! -p ${FIFO} ]; do + sleep 1 +done + +# Start the exchange +if [ $initiator = yes ]; then + ${NC} -nul -w${TIMEOUT} -p${DSTPORT} 127.0.0.1 </dev/null >packet & +# ${NC} -nu -w${TIMEOUT} -p${DSTPORT} 127.0.0.1 ${SRCPORT} </dev/null >packet + sleep 1 + echo "c udp 127.0.0.1:${DSTPORT} 2 1" >${FIFO} + in_packets=`ls ${suite}-i.* 2>/dev/null` + out_packets=`ls ${suite}-r.* 2>/dev/null` +else + in_packets=`ls ${suite}-r.* 2>/dev/null` + out_packets=`ls ${suite}-i.* 2>/dev/null` +fi +his_turn=$initiator +while [ \( $his_turn = yes -a X"$in_packets" != X \) \ + -o \( $his_turn = no -a X"$out_packets" != X \) ]; do + if [ $his_turn = no ]; then + set $out_packets + packet=$1 + shift + out_packets=$* + cat $packet |${NC} -nu -w${TIMEOUT} -p${DSTPORT} 127.0.0.1 ${SRCPORT} \ + >packet + my_turn=no + else + set $in_packets + packet=$1 + shift + in_packets=$* + if ! cmp $packet packet 2>/dev/null; then + retval=1 + break + fi + my_turn=yes + fi +done +kill $isakmpd_pid +rm -f ${FIFO} +[ $clean = yes ] && rm -f packet +exit $retval diff --git a/sbin/isakmpd/regress/group/Makefile b/sbin/isakmpd/regress/group/Makefile new file mode 100644 index 00000000000..85ac9019fab --- /dev/null +++ b/sbin/isakmpd/regress/group/Makefile @@ -0,0 +1,14 @@ +# Test Group: + +PROG= grouptest +SRCS= math_2n.c math_ec2n.c math_group.c grouptest.c util.c \ + log.c sysdep.c gmp_util.c +.PATH: ${.CURDIR}/../../ +NOMAN= +TOPOBJ!= cd ${.CURDIR}/../..; printf "all:\n\t@pwd\n" |${MAKE} -f- +CFLAGS+= -I${TOPOBJ} -I${.CURDIR}/../../ -Wall +LDADD+= -lgmp +DPADD+= ${LIBGMP} +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/group/grouptest.c b/sbin/isakmpd/regress/group/grouptest.c new file mode 100644 index 00000000000..7d4b16bb612 --- /dev/null +++ b/sbin/isakmpd/regress/group/grouptest.c @@ -0,0 +1,125 @@ +/* $Id: grouptest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * This module exercises the operations supplied by the group abstraction. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "math_2n.h" +#include "math_ec2n.h" +#include "math_group.h" + +#define DUMP_X(_x_) point = (_x_); b2n_print (point->x); + +int +main (void) +{ + int i; + char buf[100]; + char buf2[100]; + struct group *group, *group2; + ec2np_ptr point; + + group_init (); + group = group_get (3); + group2 = group_get (3); + + printf ("Testing: setraw, getraw: "); + for (i = 0; i < 20; i++) + buf[i] = i; + + group->setraw (group, group->c, buf, 20); + if (group->getlen (group) != 20) + printf ("FAILED "); + else + printf ("OKAY "); + + group->getraw (group, group->c, buf2); + for (i = 0; i < 20; i++) + if (buf2[i] != i) + break; + if (i < 20) + printf ("FAILED "); + else + printf ("OKAY "); + + printf ("\nTesting: setrandom: "); + group->setrandom (group, group->c); + DUMP_X (group->c); + group2->setrandom (group2, group2->c); + DUMP_X (group2->c); + + printf ("\nTesting: operation:\n"); + group->operation (group, group->a, group->gen, group->c); + point = group->a; + printf ("\tX (%d): ", point->x->chunks); b2n_print (point->x); + printf ("\tY (%d): ", point->y->chunks); b2n_print (point->y); + + group2->operation (group2, group2->a, group2->gen, group2->c); + point = group2->a; + printf ("\tX (%d): ", point->x->chunks); b2n_print (point->x); + printf ("\tY (%d): ", point->y->chunks); b2n_print (point->y); + + printf ("Exchange Value 1: "); b2n_print (group->d); + printf ("Exchange Value 2: "); b2n_print (group2->d); + + printf ("Testing: operation ...:\n"); + group->getraw (group, group->a, buf); + group2->setraw (group2, group2->b, buf, 20); + + group2->getraw (group2, group2->a, buf); + group->setraw (group, group->b, buf, 20); + + group2->operation (group2, group2->a, group2->b, group2->c); + printf ("Exchange Value 21: "); DUMP_X (group2->a); + + group->operation (group, group->a, group->b, group->c); + printf ("Exchange Value 12: "); DUMP_X (group->a); + + group->getraw (group, group->a, buf); + group2->getraw (group2, group2->a, buf2); + printf ("Testing: operation ...: "); + if (memcmp(buf, buf2, 20)) + printf ("FAILED "); + else + printf ("OKAY "); + + printf ("\n"); + return 1; +} diff --git a/sbin/isakmpd/regress/hmac/Makefile b/sbin/isakmpd/regress/hmac/Makefile new file mode 100644 index 00000000000..009bd692d33 --- /dev/null +++ b/sbin/isakmpd/regress/hmac/Makefile @@ -0,0 +1,10 @@ +# Test HMAC: + +PROG= hmactest +SRCS= hash.c hmactest.c +.PATH: ${.CURDIR}/../../ +NOMAN= +CFLAGS+= -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk>
\ No newline at end of file diff --git a/sbin/isakmpd/regress/hmac/hmactest.c b/sbin/isakmpd/regress/hmac/hmactest.c new file mode 100644 index 00000000000..487f5a61c3d --- /dev/null +++ b/sbin/isakmpd/regress/hmac/hmactest.c @@ -0,0 +1,97 @@ +/* $Id: hmactest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "hash.h" + +int test_hmac(char *, struct hash *, char *, int, char *, int, char *); + +#define nibble2c(x) ((x) >= 10 ? ('a'-10+(x)) : ('0' + (x))) + +int +main (void) +{ + char key[100]; + + memset(key, 11, 20); + test_hmac ("HMAC-MD5 Test Case 1", hash_get (HASH_MD5), + key, 16, "Hi There", 8, "9294727a3638bb1c13f48ef8158bfc9d"); + test_hmac ("HMAC-MD5 Test Case 2", hash_get (HASH_MD5), + "Jefe", 4, + "what do ya want for nothing?", 28, + "750c783e6ab0b503eaa86e310a5db738"); + test_hmac ("HMAC-SHA1 Test Case 1", hash_get (HASH_SHA1), + key, 20, "Hi There", 8, + "b617318655057264e28bc0b6fb378c8ef146be00"); + test_hmac ("HMAC-SHA1 Test Case 2", hash_get (HASH_SHA1), + "Jefe", 4, "what do ya want for nothing?", 28, + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"); + + return 1; +} + +int +test_hmac(char *test, struct hash *hash, char *key, int klen, + char *data, int dlen, char *cmp) +{ + char output[2*HASH_MAX+1]; + int i; + + printf("Testing %s: ", test); + + hash->HMACInit(hash, key, klen); + hash->Update(hash->ctx, data, dlen); + hash->HMACFinal(hash->digest, hash); + + for (i=0; i<hash->hashsize; i++) + { + output[2*i] = nibble2c((hash->digest[i] >> 4) & 0xf); + output[2*i+1] = nibble2c(hash->digest[i] & 0xf); + } + output[2*i] = 0; + + if (!strcmp(output, cmp)) + { + printf("OKAY\n"); + return 1; + } + + printf("%s <-> %s\n", output, cmp); + return 0; +} diff --git a/sbin/isakmpd/regress/pkcs/Makefile b/sbin/isakmpd/regress/pkcs/Makefile new file mode 100644 index 00000000000..05a9566ca77 --- /dev/null +++ b/sbin/isakmpd/regress/pkcs/Makefile @@ -0,0 +1,13 @@ +# Test PKCS#1 + +PROG= pkcstest +SRCS= log.c asn.c gmp_util.c pkcs.c pkcstest.c sysdep.c \ + asn_useful.c hash.c +.PATH: ${.CURDIR}/../../ +LDADD+= -lgmp +DPADD+= ${LIBDES} +NOMAN= +CFLAGS+= -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/pkcs/pkcstest.c b/sbin/isakmpd/regress/pkcs/pkcstest.c new file mode 100644 index 00000000000..1e1f520c6b4 --- /dev/null +++ b/sbin/isakmpd/regress/pkcs/pkcstest.c @@ -0,0 +1,124 @@ +/* $Id: pkcstest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <ctype.h> +#include <stdio.h> +#include <gmp.h> +#include <stdlib.h> +#include <string.h> + +#include "gmp_util.h" +#include "asn.h" +#include "pkcs.h" + +#define nibble2bin(y) (tolower((y)) < 'a' ? (y) - '0': tolower((y)) - 'a' + 10) +#define hexchar2bin(x) ((nibble2bin((x)[0]) << 4) + nibble2bin((x)[1])) +#define nibble2c(x) ((x) >= 10 ? ('a'-10+(x)) : ('0' + (x))) + +void asc2bin (u_int8_t *bin, u_int8_t *asc, u_int16_t len) +{ + int i; + + for (i = 0; i < len; i += 2, asc += 2) + { + *bin++ = hexchar2bin(asc); + } +} + +int +main (void) +{ + char buf[500]; + char *publickey = "304702400a66791dc6988168de7ab77419bb7fb0c001c6271027" + "0075142942e19a8d8c51d053b3e3782a1de5dc5af4ebe99468170114a1dfe67cdc9a9" + "af55d655620bbab0203010001"; + char *privatekey = "3082013602010002400a66791dc6988168de7ab77419bb7fb0c001" + "c62710270075142942e19a8d8c51d053b3e3782a1de5dc5af4ebe99468170114a1dfe67" + "cdc9a9af55d655620bbab020301000102400123c5b61ba36edb1d3679904199a89ea80c" + "09b9122e1400c09adcf7784676d01d23356a7d44d6bd8bd50e94bfc723fa87d8862b751" + "77691c11d757692df8881022033d48445c859e52340de704bcdda065fbb4058d740bd1d" + "67d29e9c146c11cf610220335e8408866b0fd38dc7002d3f972c67389a65d5d8306566d" + "5c4f2a5aa52628b0220045ec90071525325d3d46db79695e9afacc4523964360e02b119" + "baa366316241022015eb327360c7b60d12e5e2d16bdcd97981d17fba6b70db13b20b436" + "e24eada5902202ca6366d72781dfa24d34a9a24cbc2ae927a9958af426563ff63fb1165" + "8a461d"; + char *data = "Niels ist ein Luser!"; + u_int8_t *enc, *dec; + u_int16_t len; + u_int32_t enclen; + int erg = 0; + + struct rsa_public_key key; + struct rsa_private_key priv; + + asc2bin (buf, publickey, strlen (publickey)); + pkcs_public_key_from_asn (&key, buf, sizeof (buf)); + + printf ("n: 0x"); mpz_out_str (stdout, 16, key.n); + printf ("\ne: 0x"); mpz_out_str (stdout, 16, key.e); + printf ("\n"); + + asc2bin (buf, privatekey, strlen (privatekey)); + pkcs_private_key_from_asn (&priv, buf, sizeof (buf)); + + printf ("n: 0x"); mpz_out_str (stdout, 16, priv.n); + printf ("\ne: 0x"); mpz_out_str (stdout, 16, priv.e); + printf ("\nd: 0x"); mpz_out_str (stdout, 16, priv.d); + printf ("\np: 0x"); mpz_out_str (stdout, 16, priv.p); + printf ("\nq: 0x"); mpz_out_str (stdout, 16, priv.q); + printf ("\n"); + + printf ("Testing Signing/Verifying: "); + /* Sign with Private Key */ + if (!pkcs_rsa_encrypt (PKCS_PRIVATE, priv.n, priv.d, data, strlen(data)+1, + &enc, &enclen)) + printf ("FAILED "); + else + /* Decrypt/Verify with Public Key */ + erg = pkcs_rsa_decrypt (PKCS_PRIVATE, key.n, key.e, enc, &dec, &len); + + if (!erg || strcmp(data,dec)) + printf ("FAILED "); + else + printf ("OKAY "); + + printf ("\n"); + + pkcs_free_public_key (&key); + pkcs_free_private_key (&priv); + + return 1; +} diff --git a/sbin/isakmpd/regress/prf/Makefile b/sbin/isakmpd/regress/prf/Makefile new file mode 100644 index 00000000000..7d58f825d23 --- /dev/null +++ b/sbin/isakmpd/regress/prf/Makefile @@ -0,0 +1,10 @@ +# Test HMAC: + +PROG= prftest +SRCS= log.c prf.c hash.c prftest.c +.PATH: ${.CURDIR}/../../ +NOMAN= +CFLAGS+= -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/prf/prftest.c b/sbin/isakmpd/regress/prf/prftest.c new file mode 100644 index 00000000000..1ba253865aa --- /dev/null +++ b/sbin/isakmpd/regress/prf/prftest.c @@ -0,0 +1,115 @@ +/* $Id: prftest.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "hash.h" +#include "prf.h" + +int test_prf(char *, enum hashes, char *, int, char *, int, char *); + +#define nibble2c(x) ((x) >= 10 ? ('a'-10+(x)) : ('0' + (x))) + +/* + * Basically the same as the HMAC regress, but to keep with modularity + * prf seems to be useful. So here we just check the HMAC test cases, + * until there are more PRFs. + */ + +int +main (void) +{ + char key[100]; + + memset(key, 11, 20); + test_prf ("PRF MD5 Test Case 1", HASH_MD5, + key, 16, "Hi There", 8, "9294727a3638bb1c13f48ef8158bfc9d"); + test_prf ("PRF MD5 Test Case 2", HASH_MD5, + "Jefe", 4, + "what do ya want for nothing?", 28, + "750c783e6ab0b503eaa86e310a5db738"); + test_prf ("PRF SHA1 Test Case 1", HASH_SHA1, + key, 20, "Hi There", 8, + "b617318655057264e28bc0b6fb378c8ef146be00"); + test_prf ("PRF SHA1 Test Case 2", HASH_SHA1, + "Jefe", 4, "what do ya want for nothing?", 28, + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"); + + return 1; +} + +int +test_prf(char *test, enum hashes hash, char *key, int klen, + char *data, int dlen, char *cmp) +{ + char output[2*HASH_MAX+1]; + char digest[HASH_MAX]; + struct prf *prf; + int i; + + printf("Testing %s: ", test); + + prf = prf_alloc(PRF_HMAC, hash, key, klen); + if (prf == NULL) + { + printf("prf_alloc() returned NULL\n"); + return 0; + } + + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, data, dlen); + prf->Final(digest, prf->prfctx); + + prf_free(prf); + + for (i=0; i<prf->blocksize; i++) + { + output[2*i] = nibble2c((digest[i] >> 4) & 0xf); + output[2*i+1] = nibble2c(digest[i] & 0xf); + } + output[2*i] = 0; + + if (!strcmp(output, cmp)) + { + printf("OKAY\n"); + return 1; + } + + printf("%s <-> %s\n", output, cmp); + return 0; +} diff --git a/sbin/isakmpd/regress/rsakeygen/Makefile b/sbin/isakmpd/regress/rsakeygen/Makefile new file mode 100644 index 00000000000..95db8b1c9a6 --- /dev/null +++ b/sbin/isakmpd/regress/rsakeygen/Makefile @@ -0,0 +1,12 @@ +# RSA Key Generation + +PROG= rsakeygen +SRCS= log.c asn.c gmp_util.c pkcs.c rsakeygen.c sysdep.c +.PATH: ${.CURDIR}/../../ +LDADD+= -lgmp +DPADD+= ${LIBDES} +NOMAN= +CFLAGS+= -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/rsakeygen/rsakeygen.c b/sbin/isakmpd/regress/rsakeygen/rsakeygen.c new file mode 100644 index 00000000000..665a5c25fe2 --- /dev/null +++ b/sbin/isakmpd/regress/rsakeygen/rsakeygen.c @@ -0,0 +1,121 @@ +/* $Id: rsakeygen.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <gmp.h> + +#include "log.h" +#include "gmp_util.h" +#include "asn.h" +#include "pkcs.h" + +#define nibble2bin(y) (tolower((y)) < 'a' ? (y) - '0': tolower((y)) - 'a' + 10) +#define hexchar2bin(x) ((nibble2bin((x)[0]) << 4) + nibble2bin((x)[1])) +#define nibble2c(x) ((x) >= 10 ? ('a'-10+(x)) : ('0' + (x))) + +void asc2bin (u_int8_t *bin, u_int8_t *asc, u_int16_t len) +{ + int i; + + for (i = 0; i < len; i += 2, asc += 2) + { + *bin++ = hexchar2bin(asc); + } +} + +int +main (void) +{ + char *data = "Niels ist ein Luser!"; + u_int8_t *enc, *dec, *asn; + u_int32_t enclen; + u_int16_t len; + FILE *fd; + int erg = 0; + + struct rsa_public_key key; + struct rsa_private_key priv; + + log_debug_cmd ((enum log_classes)LOG_CRYPTO, 99); + pkcs_generate_rsa_keypair (&key, &priv, 1024); + + printf ("n: 0x"); mpz_out_str (stdout, 16, key.n); + printf ("\ne: 0x"); mpz_out_str (stdout, 16, key.e); + printf ("\n"); + + printf ("n: 0x"); mpz_out_str (stdout, 16, priv.n); + printf ("\ne: 0x"); mpz_out_str (stdout, 16, priv.e); + printf ("\nd: 0x"); mpz_out_str (stdout, 16, priv.d); + printf ("\np: 0x"); mpz_out_str (stdout, 16, priv.p); + printf ("\nq: 0x"); mpz_out_str (stdout, 16, priv.q); + printf ("\n"); + + printf ("Testing Signing/Verifying: "); + /* Sign with Private Key */ + if (!pkcs_rsa_encrypt (PKCS_PRIVATE, priv.n, priv.d, data, strlen(data)+1, + &enc, &enclen)) + printf ("FAILED "); + else + /* Decrypt/Verify with Public Key */ + erg = pkcs_rsa_decrypt (PKCS_PRIVATE, key.n, key.e, enc, &dec, &len); + + if (!erg || strcmp(data,dec)) + printf ("FAILED "); + else + printf ("OKAY "); + + printf ("\n"); + + asn = pkcs_public_key_to_asn (&key); + fd = fopen ("isakmpd_key.pub", "w"); + fwrite (asn, asn_get_len (asn), 1, fd); + fclose (fd); + free (asn); + + asn = pkcs_private_key_to_asn (&priv); + fd = fopen ("isakmpd_key", "w"); + fwrite (asn, asn_get_len (asn), 1, fd); + fclose (fd); + free (asn); + + pkcs_free_public_key (&key); + pkcs_free_private_key (&priv); + + return 1; +} diff --git a/sbin/isakmpd/regress/x509/Makefile b/sbin/isakmpd/regress/x509/Makefile new file mode 100644 index 00000000000..3a7bdc0a7b3 --- /dev/null +++ b/sbin/isakmpd/regress/x509/Makefile @@ -0,0 +1,14 @@ +# Test X509 + +PROG= x509test +SRCS= x509test.c conf.c asn.c asn_useful.c gmp_util.c log.c pkcs.c \ + sysdep.c hash.c x509.c +TOPOBJ!= cd ${.CURDIR}/../..; printf "all:\n\t@pwd\n" |${MAKE} -f- +.PATH: ${.CURDIR}/../.. ${TOPOBJ} +LDADD+= -lgmp +DPADD+= ${LIBDES} +NOMAN= +CFLAGS+= -I${.CURDIR}/../.. -I${TOPOBJ} -Wall +DEBUG= -g + +.include <bsd.prog.mk> diff --git a/sbin/isakmpd/regress/x509/certificate.txt b/sbin/isakmpd/regress/x509/certificate.txt new file mode 100644 index 00000000000..5ebe7b81482 --- /dev/null +++ b/sbin/isakmpd/regress/x509/certificate.txt @@ -0,0 +1,8 @@ +version: 2 +serialnumber: 0 +issuer1: SE +issuer2: We, our grandmother and God himself +subject1: SE +subject2: We, our grandmother and God himself +start: 980101000000Z +end: 990101000000Z diff --git a/sbin/isakmpd/regress/x509/x509test.c b/sbin/isakmpd/regress/x509/x509test.c new file mode 100644 index 00000000000..f481029adf4 --- /dev/null +++ b/sbin/isakmpd/regress/x509/x509test.c @@ -0,0 +1,165 @@ +/* $Id: x509test.c,v 1.1 1998/11/15 00:03:50 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <gmp.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "conf.h" +#include "asn.h" +#include "asn_useful.h" +#include "pkcs.h" +#include "x509.h" +#include "log.h" + +u_int32_t file_sz; + +#define LINECOL(x,y) (x) = strsep (&(y), "\n\r"); \ + (x) = strchr ((x), ':') + 1; \ + while (isspace((x)[0])) (x)++; \ + + +u_int8_t * +open_file (char *name) +{ + int fd; + struct stat st; + u_int8_t *addr; + + if (stat (name, &st) == -1) + log_fatal ("stat (\"%s\", &st)", name); + file_sz = st.st_size; + fd = open (name, O_RDONLY); + if (fd == -1) + log_fatal ("open (\"%s\", O_RDONLY)", name); + addr = mmap (0, file_sz, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, + fd, 0); + if (!addr) + log_fatal ("mmap (0, %d, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE," + "%d, 0)", file_sz, fd); + close (fd); + + return addr; +} + +int +main (void) +{ + struct rsa_private_key priv; + struct x509_certificate cert; + FILE *fd; + char *p, *p2; + u_int8_t *addr, *asn; + u_int32_t asnlen, len; + + addr = open_file ("isakmpd_key"); + if (!pkcs_private_key_from_asn (&priv, addr, asn_get_len (addr))) + { + munmap (addr, file_sz); + exit (1); + } + munmap (addr, file_sz); + + addr = open_file ("isakmpd_key.pub"); + if (!pkcs_public_key_from_asn (&cert.key, addr, asn_get_len (addr))) + { + munmap (addr, file_sz); + exit (1); + } + munmap (addr, file_sz); + + cert.signaturetype = strdup (ASN_ID_MD5WITHRSAENC); + cert.issuer1.type = strdup (ASN_ID_COUNTRY_NAME); + cert.issuer2.type = strdup (ASN_ID_ORGANIZATION_NAME); + cert.subject1.type = strdup (ASN_ID_COUNTRY_NAME); + cert.subject2.type = strdup (ASN_ID_ORGANIZATION_NAME); + + addr = open_file ("certificate.txt"); + p = addr; + + LINECOL (p2, p); cert.version = atoi (p2); + LINECOL (p2, p); cert.serialnumber = atoi (p2); + LINECOL (p2, p); cert.issuer1.val = strdup (p2); + LINECOL (p2, p); cert.issuer2.val = strdup (p2); + LINECOL (p2, p); cert.subject1.val = strdup (p2); + LINECOL (p2, p); cert.subject2.val = strdup (p2); + LINECOL (p2, p); cert.start = strdup (p2); + LINECOL (p2, p); cert.end = strdup (p2); + munmap (addr, file_sz); + + /* XXX - just put any IP number in there - XXX */ + cert.extension.type = strdup (ASN_ID_SUBJECT_ALT_NAME); + cert.extension.val = p = malloc (8); + /* XXX - this could also be encoded as norm_type, but time is lacking */ + p[0] = 0x30; p[1] = 0x06; p[2] = 0x87; p[3] = 0x04; + memset (p + 4, 0, 4); + + printf ("Encoding Certificiate: "); + if (!x509_encode_certificate(&cert, &asn, &asnlen)) + printf ("FAILED "); + else + printf ("OKAY "); + printf ("\n"); + + printf ("Creating Signature: "); + if (!x509_create_signed (asn, asnlen, &priv, &addr, &len)) + printf ("FAILED "); + else + printf ("OKAY "); + printf ("\n"); + + printf ("Validate SIGNED: "); + if (!x509_validate_signed (addr, len, &cert.key, &asn, &asnlen)) + printf ("FAILED "); + else + printf ("OKAY "); + printf ("\n"); + + fd = fopen ("cert.asn", "w"); + fwrite (addr, len, 1, fd); + fclose (fd); + + free (addr); + + return 1; +} diff --git a/sbin/isakmpd/sa.c b/sbin/isakmpd/sa.c new file mode 100644 index 00000000000..60e9b9b9770 --- /dev/null +++ b/sbin/isakmpd/sa.c @@ -0,0 +1,456 @@ +/* $Id: sa.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#include "cookie.h" +#include "doi.h" +#include "exchange.h" +#include "isakmp.h" +#include "log.h" +#include "message.h" +#include "sa.h" +#include "timer.h" +#include "transport.h" +#include "util.h" + +/* Initial number of bits from the cookies used as hash. */ +#define INITIAL_BUCKET_BITS 6 + +/* + * Don't try to use more bits than this as a hash. + * We only XOR 16 bits so going above that means changing the code below + * too. + */ +#define MAX_BUCKET_BITS 16 + +static void sa_dump (char *, struct sa *); + +static LIST_HEAD (sa_list, sa) *sa_tab; + +/* Works both as a maximum index and a mask. */ +static int bucket_mask; + +void +sa_init () +{ + int i; + + bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1; + sa_tab = malloc ((bucket_mask + 1) * sizeof (struct sa_list)); + if (!sa_tab) + log_fatal ("init_sa: out of memory"); + for (i = 0; i <= bucket_mask; i++) + { + LIST_INIT (&sa_tab[i]); + } + +} + +/* XXX Ww don't yet resize. */ +void +sa_resize () +{ + int new_mask = (bucket_mask + 1) * 2 - 1; + int i; + struct sa_list *new_tab; + + new_tab = realloc (sa_tab, (new_mask + 1) * sizeof (struct sa_list)); + if (!new_tab) + return; + sa_tab = new_tab; + for (i = bucket_mask + 1; i <= new_mask; i++) + { + LIST_INIT (&sa_tab[i]); + } + bucket_mask = new_mask; + + /* XXX Rehash existing entries. */ +} + +/* Lookup an ISAKMP SA out of just the initiator cookie. */ +struct sa * +sa_lookup_from_icookie (u_int8_t *cookie) +{ + int i; + struct sa *sa; + + for (i = 0; i < bucket_mask; i++) + for (sa = LIST_FIRST (&sa_tab[i]); sa; sa = LIST_NEXT (sa, link)) + if (memcmp (sa->cookies, cookie, ISAKMP_HDR_ICOOKIE_LEN) == 0 + && sa->phase == 1) + return sa; + return 0; +} + +int +sa_enter (struct sa *sa) +{ + u_int16_t bucket = 0; + int i; + u_int8_t *cp; + + /* XXX We might resize if we are crossing a certain threshold */ + + for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) + { + cp = sa->cookies + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) + { + cp = sa->message_id + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + bucket &= bucket_mask; + LIST_INSERT_HEAD (&sa_tab[bucket], sa, link); + return 1; +} + +/* + * Lookup the SA given by the header fields MSG. PHASE2 is false when + * looking for phase 1 SAa and true otherwise. + */ +struct sa * +sa_lookup_by_header (u_int8_t *msg, int phase2) +{ + return sa_lookup (msg + ISAKMP_HDR_COOKIES_OFF, + phase2 ? msg + ISAKMP_HDR_MESSAGE_ID_OFF : 0); +} + +/* + * Lookup the SA given by the COOKIES and possibly the MESSAGE_ID unless + * NULL, meaning we are looking for phase 1 SAs. + */ +struct sa * +sa_lookup (u_int8_t *cookies, u_int8_t *message_id) +{ + u_int16_t bucket = 0; + int i; + struct sa *sa; + u_int8_t *cp; + + /* + * We use the cookies to get bits to use as an index into sa_tab, as at + * least one (our cookie) is a good hash, xoring all the bits, 16 at a + * time, and then masking, should do. Doing it this way means we can + * validate cookies very fast thus delimiting the effects of "Denial of + * service"-attacks using packet flooding. + */ + for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) + { + cp = cookies + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + if (message_id) + for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) + { + cp = message_id + i; + /* Doing it this way avoids alignment problems. */ + bucket ^= cp[0] | cp[1] << 8; + } + bucket &= bucket_mask; + for (sa = LIST_FIRST (&sa_tab[bucket]); + sa && (memcmp (cookies, sa->cookies, ISAKMP_HDR_COOKIES_LEN) != 0 + || (message_id && memcmp (message_id, sa->message_id, + ISAKMP_HDR_MESSAGE_ID_LEN) + != 0)); + sa = LIST_NEXT (sa, link)) + ; + + return sa; +} + +/* Create a SA. */ +int +sa_create (struct exchange *exchange, struct transport *t) +{ + struct sa *sa; + + /* + * We want the SA zeroed for sa_free to be able to find out what fields + * have been filled-in. + */ + sa = calloc (1, sizeof *sa); + if (!sa) + return -1; + sa->transport = t; + sa->phase = exchange->phase; + memcpy (sa->cookies, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + memcpy (sa->message_id, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + sa->doi = exchange->doi; + + /* Allocate the DOI-specific structure and initialize it to zeroes. */ + sa->data = calloc (1, sa->doi->sa_size); + if (!sa->data) + { + free (sa); + return 0; + } + + TAILQ_INIT (&sa->protos); + + sa_enter (sa); + TAILQ_INSERT_TAIL (&exchange->sa_list, sa, next); + return 0; +} + +static void +sa_dump (char *header, struct sa *sa) +{ + struct proto *proto; + char spi_header[80]; + int i; + + log_debug (LOG_MISC, 10, "%s: phase %d doi %d msgid %08x flags 0x%x", + header, sa->phase, sa->doi->id, decode_32 (sa->message_id), + sa->flags); + log_debug (LOG_MISC, 10, + "%s: icookie %08x%08x rcookie %08x%08x", header, + decode_32 (sa->cookies), decode_32 (sa->cookies + 4), + decode_32 (sa->cookies + 8), decode_32 (sa->cookies + 12)); + for (proto = TAILQ_FIRST (&sa->protos); proto; + proto = TAILQ_NEXT (proto, link)) + { + log_debug (LOG_MISC, 10, + "%s: suite %d proto %d " + "spi_sz[0] %d spi[0] %p spi_sz[1] %d spi[1] %p", + header, proto->no, proto->proto, proto->spi_sz[0], + proto->spi[0], proto->spi_sz[1], proto->spi[1]); + for (i = 0; i < 2; i++) + if (proto->spi[i]) + { + snprintf (spi_header, 80, "%s: spi[%d]", header, i); + log_debug_buf (LOG_MISC, 10, spi_header, proto->spi[i], + proto->spi_sz[i]); + } + } +} + +void +sa_report (void) +{ + int i; + struct sa *sa; + + for (i = 0; i < bucket_mask; i++) + for (sa = LIST_FIRST (&sa_tab[i]); sa; sa = LIST_NEXT (sa, link)) + sa_dump ("sa_report", sa); +} + +void +proto_free (struct proto *proto) +{ + int i; + struct sa *sa = proto->sa; + + for (i = 0; i < 2; i++) + if (proto->spi[i]) + { + if (sa->doi->delete_spi) + sa->doi->delete_spi (sa, proto, i); + free (proto->spi[i]); + } + TAILQ_REMOVE (&sa->protos, proto, link); + if (proto->data) + { + if (sa->doi && sa->doi->free_proto_data) + sa->doi->free_proto_data (proto->data); + free (proto->data); + } + free (proto); +} + +/* Release all resources this SA is using. */ +void +sa_free (struct sa *sa) +{ + if (sa->death) + timer_remove_event (sa->death); + sa_free_aux (sa); +} + +/* Release all resources this SA is using except the death timer. */ +void +sa_free_aux (struct sa *sa) +{ + struct proto *proto; + + if (sa->data) + { + if (sa->doi && sa->doi->free_sa_data) + sa->doi->free_sa_data (sa->data); + free (sa->data); + } + if (sa->last_sent_in_setup) + message_free (sa->last_sent_in_setup); + while ((proto = TAILQ_FIRST (&sa->protos)) != 0) + proto_free (proto); + LIST_REMOVE (sa, link); + free (sa); +} + +/* + * Rehash the ISAKMP SA this MSG is negotiating with the responder cookie + * filled in. + */ +void +sa_isakmp_upgrade (struct message *msg) +{ + struct sa *sa = TAILQ_FIRST (&msg->exchange->sa_list); + + LIST_REMOVE (sa, link); + GET_ISAKMP_HDR_RCOOKIE (msg->iov[0].iov_base, + sa->cookies + ISAKMP_HDR_ICOOKIE_LEN); + /* + * We don't install a transport in the initiator case as we don't know + * what local address will be chosen. Do it now instead. + */ + sa->transport = msg->transport; + sa_enter (sa); +} + +/* + * Register the chosen transform into the SA. As a side effect set PROTOP + * to point at the corresponding proto structure. + */ +int +sa_add_transform (struct sa *sa, struct payload *xf, int initiator, + struct proto **protop) +{ + struct proto *proto; + struct payload *prop = xf->context; + + *protop = 0; + if (!initiator) + proto = calloc (1, sizeof *proto); + else + /* Find the protection suite that were chosen. */ + for (proto = TAILQ_FIRST (&sa->protos); + proto && proto->no != GET_ISAKMP_PROP_NO (prop->p); + proto = TAILQ_NEXT (proto, link)) + ; + if (!proto) + return -1; + *protop = proto; + + /* Allocate DOI-specific part. */ + if (!initiator) + { + proto->data = calloc (1, sa->doi->proto_size); + if (!proto->data) + goto cleanup; + } + + proto->no = GET_ISAKMP_PROP_NO (prop->p); + proto->proto = GET_ISAKMP_PROP_PROTO (prop->p); + proto->spi_sz[!initiator] = GET_ISAKMP_PROP_SPI_SZ (prop->p); + if (proto->spi_sz[!initiator]) + { + proto->spi[!initiator] = malloc (proto->spi_sz[!initiator]); + if (!proto->spi[!initiator]) + goto cleanup; + memcpy (proto->spi[!initiator], prop->p + ISAKMP_PROP_SPI_OFF, + proto->spi_sz[!initiator]); + } + proto->chosen = xf; + proto->sa = sa; + proto->id = GET_ISAKMP_TRANSFORM_ID (xf->p); + if (!initiator) + TAILQ_INSERT_TAIL (&sa->protos, proto, link); + return 0; + + cleanup: + if (!initiator) + { + if (proto->data) + free (proto->data); + free (proto); + } + *protop = 0; + return -1; +} + +/* Lookup an ISAKMP SA given its peer address. */ +struct sa * +sa_isakmp_lookup_by_peer (struct sockaddr *addr, size_t addr_len) +{ + int i; + struct sa *sa; + struct sockaddr *taddr; + int taddr_len; + + for (i = 0; i < bucket_mask; i++) + for (sa = LIST_FIRST (&sa_tab[i]); sa; sa = LIST_NEXT (sa, link)) + /* + * XXX We check the transport because it can be NULL until we fix + * the initiator case to set the transport always. + */ + if (sa->phase == 1 && sa->transport) + { + sa->transport->vtbl->get_dst (sa->transport, &taddr, &taddr_len); + if (taddr_len == addr_len && memcmp (taddr, addr, addr_len) == 0) + return sa; + } + return 0; +} + +/* Delete an SA. Tell the peer if NOTIFY is set. */ +void +sa_delete (struct sa *sa, int notify) +{ + /* XXX we do not send DELETE payloads just yet. */ + + sa_free (sa); +} + +/* + * Establish a new ISAKMP SA. + * XXX Whatif the peer initiated another SA negotiation? + */ +void +sa_rekey_p1 (struct sa *sa) +{ + /* XXX Fill in the args argument. */ + exchange_establish_p1 (sa->transport, sa->exch_type, sa->doi->id, 0); + + /* XXX I want this sa_delete deferred until the new SA is ready. */ + sa_delete (sa, 1); +} diff --git a/sbin/isakmpd/sa.h b/sbin/isakmpd/sa.h new file mode 100644 index 00000000000..79ddbb2137c --- /dev/null +++ b/sbin/isakmpd/sa.h @@ -0,0 +1,162 @@ +/* $Id: sa.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _SA_H_ +#define _SA_H_ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> + +#include "isakmp.h" + +/* Remove a SA if it has not been fully negotiated in this time. */ +#define SA_NEGOTIATION_MAX_TIME 120 + +struct crypto_xf; +struct doi; +struct event; +struct exchange; +struct keystate; +struct message; +struct payload; +struct sa; +struct transport; + +/* A protection suite consists of a set of protocol descriptions like this. */ +struct proto { + /* Link to the next protocol in the suite. */ + TAILQ_ENTRY (proto) link; + + /* The SA we belong to. */ + struct sa *sa; + + /* The protocol number as found in the proposal payload. */ + u_int8_t no; + + /* The protocol this SA is for. */ + u_int8_t proto; + + /* Security parameter index info. Element 0 - responder, 1 - initiator. */ + u_int8_t spi_sz[2]; + u_int8_t *spi[2]; + + /* + * The chosen transform, only valid while the incoming SA payload that held + * it is available for duplicate testing. + */ + struct payload *chosen; + + /* The chosen transform's ID. */ + u_int8_t id; + + /* DOI-specific data. */ + void *data; +}; + +struct sa { + /* Link to SAs with the same hash value. */ + LIST_ENTRY (sa) link; + + /* + * When several SA's are being negotiated in one message we connect them + * through this link. + */ + TAILQ_ENTRY (sa) next; + + /* What transport does this SA denote (ISAKMP SA only). */ + struct transport *transport; + + /* Both initiator and responder cookies. */ + u_int8_t cookies[ISAKMP_HDR_COOKIES_LEN]; + + /* The message ID signifying non-ISAKMP SAs. */ + u_int8_t message_id[ISAKMP_HDR_MESSAGE_ID_LEN]; + + /* The protection suite chosen. */ + TAILQ_HEAD (proto_head, proto) protos; + + /* The exchange type we should use when rekeying. */ + u_int8_t exch_type; + + /* Phase is 1 for ISAKMP SAs, and 2 for application ones. */ + u_int8_t phase; + + /* Save last message from setup, which can be retransmitted for dups */ + struct message *last_sent_in_setup; + + /* Various flags, look below for descriptions. */ + u_int32_t flags; + + /* The DOI that is to handle DOI-specific issues for this SA. */ + struct doi *doi; + + /* Crypto info needed to encrypt/decrypt packets protected by this SA. */ + struct crypto_xf *crypto; + int key_length; + struct keystate *keystate; + + /* DOI-specific opaque data. */ + void *data; + + /* Lifetime data. */ + u_int64_t seconds; + u_int64_t kilobytes; + + /* The event that will occur when an SA has timed out. */ + struct event *death; +}; + +/* This SA is alive. */ +#define SA_FLAG_READY 1 + +extern void proto_free (struct proto *proto); +extern int sa_add_transform (struct sa *, struct payload *, int, + struct proto **); +extern int sa_create (struct exchange *, struct transport *); +extern void sa_delete (struct sa *, int); +extern void sa_free (struct sa *); +extern void sa_free_aux (struct sa *); +extern void sa_init (void); +extern struct sa *sa_isakmp_lookup_by_peer (struct sockaddr *, size_t addr); +extern void sa_isakmp_upgrade (struct message *); +extern struct sa *sa_lookup (u_int8_t *, u_int8_t *); +extern struct sa *sa_lookup_by_header (u_int8_t *, int); +extern struct sa *sa_lookup_from_icookie (u_int8_t *); +extern void sa_rekey_p1 (struct sa *); +extern void sa_report (void); + +#endif /* _SA_H_ */ diff --git a/sbin/isakmpd/sysdep-openbsd.c b/sbin/isakmpd/sysdep-openbsd.c new file mode 100644 index 00000000000..988a41e3b55 --- /dev/null +++ b/sbin/isakmpd/sysdep-openbsd.c @@ -0,0 +1,167 @@ +/* $Id: sysdep-openbsd.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/encap.h> +#include <stdlib.h> +#include <string.h> + +#ifdef NEED_SYSDEP_APP +#include "app.h" +#include "ipsec.h" +#include "pf_encap.h" +#endif NEED_SYSDEP_APP +#include "log.h" +#include "sysdep.h" + +extern char *__progname; +int regrand = 0; + +u_int32_t +sysdep_random () +{ + if (!regrand) + return arc4random (); + else + return random(); +} + +char * +sysdep_progname () +{ + return __progname; +} + +/* As regress/ use this file I protect the sysdep_app_* stuff like this. */ +#ifdef NEED_SYSDEP_APP +int +sysdep_app_open () +{ + return pf_encap_open (); +} + +void +sysdep_app_handler (int fd) +{ + pf_encap_handler (fd); +} + +u_int8_t * +sysdep_ipsec_get_spi (size_t *sz, u_int8_t proto, void *id, size_t id_sz) +{ + if (app_none) + { + *sz = IPSEC_SPI_SIZE; + /* XXX should be random instead I think. */ + return strdup ("\x12\x34\x56\x78"); + } + return pf_encap_get_spi (sz, proto, id, id_sz); +} + +int +sysdep_cleartext (int fd) +{ + int level; + + if (app_none) + return 0; + + /* + * Need to bypass system security policy, so I can send and + * receive key management datagrams in the clear. + */ + level = IPSEC_LEVEL_BYPASS; + if (setsockopt (fd, IPPROTO_IP, IP_AUTH_LEVEL, (char *)&level, sizeof level) + == -1) + { + log_error ("sysdep_cleartext: " + "setsockopt (%d, IPPROTO_IP, IP_AUTH_LEVEL, ...) failed", fd); + return -1; + } + if (setsockopt (fd, IPPROTO_IP, IP_ESP_TRANS_LEVEL, (char *)&level, + sizeof level) == -1) + { + log_error ("sysdep_cleartext: " + "setsockopt (%d, IPPROTO_IP, IP_ESP_TRANS_LEVEL, ...) " + "failed", fd); + return -1; + } + if (setsockopt (fd, IPPROTO_IP, IP_ESP_NETWORK_LEVEL, (char *)&level, + sizeof level) == -1) + { + log_error("sysdep_cleartext: " + "setsockopt (%d, IPPROTO_IP, IP_ESP_NETWORK_LEVEL, ...) " + "failed", fd); + return -1; + } + return 0; +} + +int +sysdep_ipsec_delete_spi (struct sa *sa, struct proto *proto, int initiator) +{ + if (app_none) + return 0; + return pf_encap_delete_spi (sa, proto, initiator); +} + +int +sysdep_ipsec_enable_spi (struct sa *sa, int initiator) +{ + if (app_none) + return 0; + return pf_encap_enable_spi (sa, initiator); +} + +int +sysdep_ipsec_group_spis (struct sa *sa, struct proto *proto1, + struct proto *proto2, int role) +{ + if (app_none) + return 0; + return pf_encap_group_spis (sa, proto1, proto2, role); +} + +int +sysdep_ipsec_set_spi (struct sa *sa, struct proto *proto, int role, + int initiator) +{ + if (app_none) + return 0; + return pf_encap_set_spi (sa, proto, role, initiator); +} +#endif diff --git a/sbin/isakmpd/sysdep.h b/sbin/isakmpd/sysdep.h new file mode 100644 index 00000000000..3467ccb96b0 --- /dev/null +++ b/sbin/isakmpd/sysdep.h @@ -0,0 +1,56 @@ +/* $Id: sysdep.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _SYSDEP_H_ +#define _SYSDEP_H_ + +#include <sys/types.h> + +struct proto; +struct sa; + +extern void sysdep_app_handler (int); +extern int sysdep_app_open (void); +extern int sysdep_cleartext (int); +extern int sysdep_ipsec_delete_spi (struct sa *, struct proto *, int); +extern int sysdep_ipsec_enable_spi (struct sa *, int); +extern u_int8_t *sysdep_ipsec_get_spi (size_t *, u_int8_t, void *, size_t); +extern int sysdep_ipsec_group_spis (struct sa *, struct proto *, + struct proto *, int); +extern int sysdep_ipsec_set_spi (struct sa *, struct proto *, int, int); +extern char *sysdep_progname (void); +extern u_int32_t sysdep_random (void); + +#endif /* _SYSDEP_H_ */ diff --git a/sbin/isakmpd/timer.c b/sbin/isakmpd/timer.c new file mode 100644 index 00000000000..5a6ae9714a3 --- /dev/null +++ b/sbin/isakmpd/timer.c @@ -0,0 +1,126 @@ +/* $Id: timer.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/queue.h> +#include <stdlib.h> +#include <string.h> + +#include "log.h" +#include "timer.h" + +static TAILQ_HEAD (event_list, event) events; + +void +timer_init () +{ + TAILQ_INIT (&events); +} + +void +timer_next_event (struct timeval **timeout) +{ + struct timeval now; + + if (TAILQ_FIRST (&events)) + { + gettimeofday (&now, 0); + if (timercmp (&now, &TAILQ_FIRST (&events)->expiration, >=)) + timerclear (*timeout); + else + timersub (&TAILQ_FIRST (&events)->expiration, &now, *timeout); + } + else + *timeout = 0; +} + +void +timer_handle_expirations () +{ + struct timeval now; + struct event *n; + + gettimeofday (&now, 0); + for (n = TAILQ_FIRST (&events); n && timercmp (&now, &n->expiration, >=); + n = TAILQ_FIRST (&events)) + { + log_debug (LOG_TIMER, 10, + "timer_handle_expirations: event %s(%p)", n->name, n->arg); + TAILQ_REMOVE (&events, n, link); + (*n->func) (n->arg); + free (n); + } +} + +struct event * +timer_add_event (char *name, void (*func) (void *), void *arg, + struct timeval *expiration) +{ + struct event *ev = (struct event *)malloc (sizeof *ev); + struct event *n; + + if (!ev) + return 0; + ev->name = name; + ev->func = func; + ev->arg = arg; + memcpy (&ev->expiration, expiration, sizeof *expiration); + for (n = TAILQ_FIRST (&events); + n && timercmp (expiration, &n->expiration, >=); + n = TAILQ_NEXT (n, link)) + ; + if (n) + { + log_debug (LOG_TIMER, 10, + "timer_add_event: event %s(%p) added before %s(%p)", name, + arg, n->name, n->arg); + TAILQ_INSERT_BEFORE (n, ev, link); + } + else + { + log_debug (LOG_TIMER, 10, "timer_add_event: event %s(%p) added last", + name, arg); + TAILQ_INSERT_TAIL (&events, ev, link); + } + return ev; +} + +void +timer_remove_event (struct event *ev) +{ + log_debug (LOG_TIMER, 10, "timer_remove_event: removing event %s(%p)", + ev->name, ev->arg); + TAILQ_REMOVE (&events, ev, link); + free (ev); +} diff --git a/sbin/isakmpd/timer.h b/sbin/isakmpd/timer.h new file mode 100644 index 00000000000..50fe424f9d7 --- /dev/null +++ b/sbin/isakmpd/timer.h @@ -0,0 +1,58 @@ +/* $Id: timer.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/time.h> + +struct event { + TAILQ_ENTRY (event) link; + char *name; + void (*func) (void *); + void *arg; + struct timeval expiration; +}; + +extern void timer_init (void); +extern void timer_next_event (struct timeval **); +extern void timer_handle_expirations (void); +extern struct event *timer_add_event (char *, void (*) (void *), void *, + struct timeval *); +extern void timer_remove_event (struct event *); + +#endif /* _TIMER_H_ */ diff --git a/sbin/isakmpd/transport.c b/sbin/isakmpd/transport.c new file mode 100644 index 00000000000..d523b1dcccf --- /dev/null +++ b/sbin/isakmpd/transport.c @@ -0,0 +1,245 @@ +/* $Id: transport.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/queue.h> + +#include "conf.h" +#include "exchange.h" +#include "log.h" +#include "message.h" +#include "sa.h" +#include "timer.h" +#include "transport.h" + +LIST_HEAD (transport_list, transport) transport_list; +LIST_HEAD (transport_method_list, transport_vtbl) transport_method_list; + +/* Initialize the transport maintenance module. */ +void +transport_init (void) +{ + LIST_INIT (&transport_list); + LIST_INIT (&transport_method_list); +} + +/* Register another transport T. */ +void +transport_add (struct transport *t) +{ + TAILQ_INIT (&t->sendq); + LIST_INSERT_HEAD (&transport_list, t, link); +} + +/* Register another transport method T. */ +void +transport_method_add (struct transport_vtbl *t) +{ + LIST_INSERT_HEAD (&transport_method_list, t, link); +} + +/* Apply a function FUNC on all registered transports. */ +void +transport_map (void (*func) (struct transport *)) +{ + struct transport *t; + + for (t = LIST_FIRST (&transport_list); t; t = LIST_NEXT (t, link)) + (*func) (t); +} + +/* + * Build up a file desciptor set FDS with all transport descriptors we want + * to read from. Return the number of file descriptors select(2) needs to + * check in order to cover the ones we setup in here. + */ +int +transport_fd_set (fd_set *fds) +{ + int n; + int max = -1; + struct transport *t; + + for (t = LIST_FIRST (&transport_list); t; t = LIST_NEXT (t, link)) + if (t->flags & TRANSPORT_LISTEN) + { + n = (*t->vtbl->fd_set) (t, fds); + if (n > max) + max = n; + } + return max + 1; +} + +/* + * Build up a file desciptor set FDS with all the descriptors belonging to + * transport where messages are queued for transmittal. Return the number + * of file descriptors select(2) needs to check in order to cover the ones + * we setup in here. + */ +int +transport_pending_wfd_set (fd_set *fds) +{ + int n; + int max = -1; + struct transport *t; + + for (t = LIST_FIRST (&transport_list); t; t = LIST_NEXT (t, link)) + { + if (TAILQ_FIRST (&t->sendq)) + { + n = (*t->vtbl->fd_set) (t, fds); + if (n > max) + max = n; + } + } + return max + 1; +} + +/* + * For each transport with a file descriptor in FDS, try to get an + * incoming message and start processing it. + */ +void +transport_handle_messages (fd_set *fds) +{ + struct transport *t; + + for (t = LIST_FIRST (&transport_list); t; t = LIST_NEXT (t, link)) + if ((t->flags & TRANSPORT_LISTEN) && (*t->vtbl->fd_isset) (t, fds)) + (*t->vtbl->handle_message) (t); +} + +/* + * Send the first queued message on the transports whose file descriptor + * is in FDS. + */ +void +transport_send_messages (fd_set *fds) +{ + struct transport *t; + struct message *msg; + struct timeval expiration; + struct sa *sa, *next_sa; + int expiry; + + for (t = LIST_FIRST (&transport_list); t; t = LIST_NEXT (t, link)) + if (TAILQ_FIRST (&t->sendq) && (*t->vtbl->fd_isset) (t, fds)) + { + msg = TAILQ_FIRST (&t->sendq); + TAILQ_REMOVE (&t->sendq, msg, link); + + /* + * We disregard the potential error message here, hoping that the + * retransmit will go better. + * XXX Consider a retry/fatal error discriminator. + */ + t->vtbl->send_message (msg); + + /* + * If this is not a retransmit call post-send functions that allows + * parallel work to be done while the network and peer does their + * share of the job. + */ + if (msg->xmits == 0) + message_post_send (msg); + + msg->xmits++; + + if ((msg->flags & MSG_NO_RETRANS) == 0) + { + /* XXX make this a configurable parameter. */ + if (msg->xmits > conf_get_num ("General", "retransmits")) + { + log_print ("transport_send_messages: giving up on message %p", + msg); + msg->exchange->last_sent = 0; + + /* + * As this exchange never went to a normal end, remove the + * SA's being negotiated too. + */ + for (sa = TAILQ_FIRST (&msg->exchange->sa_list); sa; + sa = next_sa) + { + next_sa = TAILQ_NEXT (sa, next); + sa_free (sa); + } + + exchange_free (msg->exchange); + message_free (msg); + return; + }; + + gettimeofday (&expiration, 0); + /* XXX Calculate from round trip timings and a backoff func. */ + expiry = msg->xmits * 2 + 5; + expiration.tv_sec += expiry; + log_debug (LOG_TRANSPORT, 30, + "transport_send_messages: " + "message %p scheduled for retransmission %d in %d secs", + msg, msg->xmits, expiry); + msg->retrans = timer_add_event ("message_send", + (void (*) (void *))message_send, + msg, &expiration); + if (!msg->retrans) + { + /* If we can make no retransmission, we can't.... */ + message_free (msg); + return; + } + + msg->exchange->last_sent = msg; + } + else if ((msg->flags & MSG_KEEP) == 0) + message_free (msg); + } +} + +/* + * Textual search after the transport method denoted by NAME, then create + * a transport connected to the peer with address ADDR, given in a transport- + * specific string format. + */ +struct transport * +transport_create (char *name, char *addr) +{ + struct transport_vtbl *method; + + for (method = LIST_FIRST (&transport_method_list); method; + method = LIST_NEXT (method, link)) + if (strcmp (method->name, name) == 0) + return (*method->create) (addr); + return 0; +} diff --git a/sbin/isakmpd/transport.h b/sbin/isakmpd/transport.h new file mode 100644 index 00000000000..8b6ee29fb60 --- /dev/null +++ b/sbin/isakmpd/transport.h @@ -0,0 +1,118 @@ +/* $Id: transport.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * The transport module tries to separate out details concerning the + * actual transferral of ISAKMP messages to other parties. + */ + +#ifndef _TRANSPORT_H_ +#define _TRANSPORT_H_ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/socket.h> + +struct message; +struct transport; + +/* This describes a tranport "method" like UDP or similar. */ +struct transport_vtbl { + /* All transport methods are linked together. */ + LIST_ENTRY (transport_vtbl) link; + + /* A textual name of the transport method. */ + char *name; + + /* Create a transport instance of this method. */ + struct transport *(*create) (char *); + + /* Let the given transport set it's bit in the fd_set passed in. */ + int (*fd_set) (struct transport *, fd_set *); + + /* Is the given transport ready for I/O? */ + int (*fd_isset) (struct transport *, fd_set *); + + /* + * Read a message from the transport's incoming pipe and start + * handling it. + */ + void (*handle_message) (struct transport *); + + /* Send a message through the outgoing pipe. */ + int (*send_message) (struct message *); + + /* + * Fill out a sockaddr structure with the transport's destination end's + * address info. XXX Why not size_t * as last arg? + */ + void (*get_dst) (struct transport *, struct sockaddr **, int *); + + /* + * Fill out a sockaddr structure with the transport's source end's + * address info. XXX Why not size_t * as last arg? + */ + void (*get_src) (struct transport *, struct sockaddr **, int *); +}; + +struct transport { + /* All transports used are linked together. */ + LIST_ENTRY (transport) link; + + /* What transport method is this an instance of? */ + struct transport_vtbl *vtbl; + + /* The queue holding messages to send on this transport. */ + TAILQ_HEAD (msg_head, message) sendq; + + /* Flags describing the transport. */ + int flags; +}; + +/* Set if this is a transport we want to listen on. */ +#define TRANSPORT_LISTEN 1 + +extern void transport_add (struct transport *); +extern struct transport *transport_create (char *, char *); +extern int transport_fd_set (fd_set *); +extern void transport_handle_messages (fd_set *); +extern void transport_init (void); +extern void transport_map (void (*) (struct transport *)); +extern void transport_method_add (struct transport_vtbl *); +extern int transport_pending_wfd_set (fd_set *); +extern void transport_send_messages (fd_set *); + +#endif /* _TRANSPORT_H_ */ diff --git a/sbin/isakmpd/udp.c b/sbin/isakmpd/udp.c new file mode 100644 index 00000000000..e8cf36e9a8d --- /dev/null +++ b/sbin/isakmpd/udp.c @@ -0,0 +1,411 @@ +/* $Id: udp.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "if.h" +#include "isakmp.h" +#include "log.h" +#include "message.h" +#include "sysdep.h" +#include "transport.h" +#include "udp.h" +#include "util.h" + +#define BACKLOG 16 +#define UDP_SIZE 65536 + +/* XXX IPv4 specific. */ +struct udp_transport { + struct transport transport; + struct sockaddr_in src; + struct sockaddr_in dst; + int s; +}; + +static struct transport *udp_clone (struct udp_transport *, + struct sockaddr_in *); +static struct transport *udp_create (char *); +static int udp_fd_set (struct transport *, fd_set *); +static int udp_fd_isset (struct transport *, fd_set *); +static void udp_handle_message (struct transport *); +static struct transport *udp_make (struct sockaddr_in *); +static int udp_send_message (struct message *); +static void udp_get_dst (struct transport *, struct sockaddr **, int *); +static void udp_get_src (struct transport *, struct sockaddr **, int *); + +struct transport_vtbl udp_transport_vtbl = { + { 0 }, "udp", + udp_create, + udp_fd_set, + udp_fd_isset, + udp_handle_message, + udp_send_message, + udp_get_dst, + udp_get_src +}; + +in_port_t udp_default_port = 0; +in_port_t udp_bind_port = 0; +static int udp_proto; +static struct transport *default_transport; + +/* Create a UDP transport structure bound to LADDR just for listening. */ +static struct transport * +udp_make (struct sockaddr_in *laddr) +{ + struct udp_transport *t = 0; + int s; + int on; + + t = malloc (sizeof *t); + if (!t) + { + log_print ("udp_make: malloc (%d) failed", sizeof *t); + return 0; + } + + s = socket (AF_INET, SOCK_DGRAM, udp_proto); + if (s == -1) + { + log_error ("udp_make: socket (%d, %d, %d)", AF_INET, SOCK_DGRAM, + udp_proto); + goto err; + } + + /* Make sure we don't get our traffic encrypted. */ + sysdep_cleartext (s); + + on = 1; + /* XXX Is this redundant considering SO_REUSEPORT below? */ + if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof on) == -1) + { + log_error ("udp_make: setsockopt (%d, %d, %d, %p, %d)", s, SOL_SOCKET, + SO_REUSEADDR, &on, sizeof on); + goto err; + } + + t->transport.vtbl = &udp_transport_vtbl; + memcpy (&t->src, laddr, sizeof t->src); + if (bind (s, (struct sockaddr *)&t->src, sizeof t->src)) + { + log_error ("udp_make: bind (%d, %p, %d)", s, &t->src, sizeof t->src); + goto err; + } + + memset (&t->dst, 0, sizeof t->dst); + t->s = s; + t->transport.flags |= TRANSPORT_LISTEN; + + transport_add ((struct transport *)t); + return (struct transport *)t; + +err: + if (s != -1) + close (s); + if (t) + free (t); + return 0; +} + +/* Clone a listen transport U, record a destination RADDR for outbound use. */ +static struct transport * +udp_clone (struct udp_transport *u, struct sockaddr_in *raddr) +{ + struct transport *t; + struct udp_transport *u2; + + t = malloc (sizeof *u); + if (!t) + return 0; + u2 = (struct udp_transport *)t; + + memcpy (t, u, sizeof *u); + memcpy (&u2->dst, raddr, sizeof u2->dst); + t->flags &= ~TRANSPORT_LISTEN; + + transport_add (t); + + return t; +} + +/* + * Initialize an object of the UDP transport class. Fill in the local + * IP address and port information and create a server socket bound to + * that specific port. Add the polymorphic transport structure to the + * system-wide pools of known ISAKMP transports. + */ +static struct transport * +udp_bind (in_addr_t addr, in_port_t port) +{ + struct sockaddr_in src; + + memset (&src, 0, sizeof src); + src.sin_len = sizeof src; + src.sin_family = AF_INET; + src.sin_addr.s_addr = addr; + src.sin_port = port; + return udp_make (&src); +} + +/* + * When looking at a specific network interface address, if it's an INET one, + * create an UDP server socket bound to it. + */ +static void +udp_bind_if (struct ifreq *ifrp, void *arg) +{ + in_port_t port = *(in_port_t *)arg; + + /* + * Well UDP is an internet protocol after all so drop other ifreqs. + * XXX IPv6 support is missing. + */ + if (ifrp->ifr_addr.sa_family != AF_INET + || ifrp->ifr_addr.sa_len != sizeof (struct sockaddr_in)) + return; + + /* XXX Deal with errors better. */ + udp_bind (((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr, port); +} + +/* + * ADDR is either a numeric IP address (we cannot risk doing DNS lookups + * which might take considerable time) or an address and a UDP port number + * separated by a colon. That address specifies a peer ISAKMP daemon we + * will talk to. Setup and return a transport useable to talk to that + * service. + */ +static struct transport * +udp_create (char *addr_arg) +{ + struct sockaddr_in dst; + char *addr_str, *port_str; + in_addr_t addr; + in_port_t port; + + addr_str = strdup (addr_arg); + if (!addr_str) + { + log_print ("udp_create: strdup (\"%s\") failed", addr_arg); + return 0; + } + port_str = strchr (addr_str, ':'); + if (port_str) + { + *port_str++ = '\0'; + port = atoi (port_str); + if (!port) + log_print ("udp_create: connection to port 0 not allowed"); + } + else + port = UDP_DEFAULT_PORT; + addr = inet_addr (addr_str); + if (addr == INADDR_NONE) + { + log_print ("udp_create: inet_addr (\"%s\") failed", addr_str); + return 0; + } + + memset (&dst, 0, sizeof dst); + dst.sin_len = sizeof dst; + dst.sin_family = AF_INET; + dst.sin_addr.s_addr = addr; + dst.sin_port = htons (port); + + return udp_clone ((struct udp_transport *)default_transport, &dst); +} + +/* + * Find out the magic numbers for the UDP protocol as well as the UDP port + * to use. Setup an UDP server for each address of this machine, and one + * for the generic case when we are the initiator. + */ +void +udp_init () +{ + struct protoent *p; + struct servent *s; + in_port_t port; + + /* Initialize the protocol and port numbers. */ + p = getprotobyname ("udp"); + udp_proto = p ? p->p_proto : IPPROTO_UDP; + if (udp_default_port) + port = htons (udp_default_port); + else + { + s = getservbyname("isakmp", "udp"); + port = s ? s->s_port : htons (UDP_DEFAULT_PORT); + } + + /* Bind the ISAKMP UDP port on all network interfaces we have. */ + /* XXX need to check errors */ + if_map (udp_bind_if, &port); + + /* + * Bind to INADDR_ANY in case of new addresses popping up. + * XXX We should use packets coming in on this socket as a signal + * to reprobe for new interfaces. + */ + default_transport = udp_bind (INADDR_ANY, port); + if (!default_transport) + log_error ("udp_init: could not allocate default ISAKMP UDP port"); + + transport_method_add (&udp_transport_vtbl); +} + +/* + * Set transport T's socket in FDS, return a value useable by select(2) + * as the number of file descriptors to check. + */ +static int +udp_fd_set (struct transport *t, fd_set *fds) +{ + struct udp_transport *u = (struct udp_transport *)t; + + FD_SET (u->s, fds); + return u->s + 1; +} + +/* Check if transport T's socket is set in FDS. */ +static int +udp_fd_isset (struct transport *t, fd_set *fds) +{ + struct udp_transport *u = (struct udp_transport *)t; + + return FD_ISSET (u->s, fds); +} + +/* + * A message has arrived on transport T's socket. If T is single-ended, + * clone it into a double-ended transport which we will use from now on. + * Package the message as we want it and continue processing in the message + * module. + * XXX We will be leaking transports unless we kill them after last + * probable use, i.e. when ISAKMP SA's gets torn down. + */ +static void +udp_handle_message (struct transport *t) +{ + struct udp_transport *u = (struct udp_transport *)t; + u_int8_t buf[UDP_SIZE]; + struct sockaddr_in from; + int len = sizeof from; + ssize_t n; + struct message *msg; + + n = recvfrom (u->s, buf, UDP_SIZE, 0, (struct sockaddr *)&from, &len); + if (n == -1) + { + log_error ("recvfrom (%d, %p, %d, %d, %p, %p)", u->s, buf, UDP_SIZE, 0, + &from, &len); + return; + } + + /* + * Make a specialized UDP transport structure out of the incoming + * transport and the address information we got from recvfrom(2). + */ + t = udp_clone (u, &from); + if (!t) + /* XXX Should we do more here? */ + return; + + msg = message_alloc (t, buf, n); + if (!msg) + /* XXX Log. */ + return; + message_recv (msg); +} + +/* Physically send the message MSG over its associated transport. */ +static int +udp_send_message (struct message *msg) +{ + struct udp_transport *u = (struct udp_transport *)msg->transport; + ssize_t n; + struct msghdr m; + + /* + * Sending on connected sockets requires that no destination address is + * given, or else EISCONN will occur. + */ + m.msg_name = (caddr_t)&u->dst; + m.msg_namelen = sizeof u->dst; + m.msg_iov = msg->iov; + m.msg_iovlen = msg->iovlen; + m.msg_control = 0; + m.msg_controllen = 0; + m.msg_flags = 0; + n = sendmsg (u->s, &m, 0); + if (n == -1) + { + log_error ("sendmsg (%d, %p, %d)", u->s, &m, 0); + return -1; + } + return 0; +} + +/* + * Get transport T's peer address and stuff it into the sockaddr pointed + * to by DST. Put its length into DST_LEN. + */ +static void +udp_get_dst (struct transport *t, struct sockaddr **dst, int *dst_len) +{ + *dst = (struct sockaddr *)&((struct udp_transport *)t)->dst; + *dst_len = sizeof ((struct udp_transport *)t)->dst; +} + +/* + * Get transport T's local address and stuff it into the sockaddr pointed + * to by SRC. Put its length into SRC_LEN. + */ +static void +udp_get_src (struct transport *t, struct sockaddr **src, int *src_len) +{ + *src = (struct sockaddr *)&((struct udp_transport *)t)->src; + *src_len = sizeof ((struct udp_transport *)t)->src; +} diff --git a/sbin/isakmpd/udp.h b/sbin/isakmpd/udp.h new file mode 100644 index 00000000000..de615f6e3ca --- /dev/null +++ b/sbin/isakmpd/udp.h @@ -0,0 +1,44 @@ +/* $Id: udp.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _UDP_H_ +#define _UDP_H_ + +extern in_port_t udp_default_port; +extern in_port_t udp_bind_port; + +extern void udp_init (void); + +#endif /* _UDP_H_ */ diff --git a/sbin/isakmpd/ui.c b/sbin/isakmpd/ui.c new file mode 100644 index 00000000000..f86fd0c5c65 --- /dev/null +++ b/sbin/isakmpd/ui.c @@ -0,0 +1,307 @@ +/* $Id: ui.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define BUF_SZ 256 + +#include "doi.h" +#include "exchange.h" +#include "isakmp.h" +#include "log.h" +#include "sa.h" +#include "transport.h" +#include "ui.h" +#include "util.h" + +char *ui_fifo = FIFO; +int ui_socket; + +/* Create and open the FIFO used for user control. */ +void +ui_init () +{ + /* -f- means control messages comes in via stdin. */ + if (strcmp (ui_fifo, "-") == 0) + ui_socket = 0; + else + { + /* No need to know about errors. */ + unlink (ui_fifo); + if (mkfifo (ui_fifo, 0600) == -1) + log_fatal ("mkfifo"); + + /* XXX Is O_RDWR needed on some OSes? Photurisd seems to imply that. */ + ui_socket = open (ui_fifo, O_RDONLY | O_NONBLOCK, 0); + if (ui_socket == -1) + log_fatal (ui_fifo); + } +} + +/* Parse the connect command found in CMD. */ +static void +ui_connect (char *cmd) +{ + char trpt[11]; + char addr[81]; + struct transport *transport = 0; + int exchange, doi; + struct sa *isakmp_sa = 0; + u_int8_t cookies[ISAKMP_HDR_COOKIES_LEN]; + + if (sscanf (cmd, "c %10s %80s %d %d", trpt, addr, &exchange, &doi) != 4) + { + log_print ("ui_connect: command \"%s\" malformed", cmd); + return; + } + + if (strcasecmp (trpt, "isakmp") == 0) + { + if (hex2raw (addr, cookies, ISAKMP_HDR_COOKIES_LEN) == -1) + { + log_print ("ui_connect: cookiepair \"%s\" malformed", addr); + return; + } + isakmp_sa = sa_lookup (cookies, 0); + if (!isakmp_sa) + { + log_print ("ui_connect: transport \"%s %s\" could not be found", + trpt, addr); + return; + } + + /* XXX Fill in the args argument. */ + exchange_establish_p2 (isakmp_sa, exchange, 0); + } + else + { + transport = transport_create (trpt, addr); + if (!transport) + { + log_print ("ui_connect: transport \"%s %s\" could not be created", + trpt, addr); + return; + } + + /* XXX This validity check seems to be a bit dumb. */ +#if 0 + /* Only ISAKMP exchange types can be given. */ + if (exchange < ISAKMP_EXCH_BASE || exchange >= ISAKMP_EXCH_FUTURE_MIN) + { + log_print ("exchange %d is not valid", exchange); + return; + } +#endif + + /* Only valid DOIs can be given. XXX Uninteresting for phase 2. */ + if (!doi_lookup (doi)) + { + log_print ("DOI %d is not valid", doi); + return; + } + + /* XXX Fill in the args argument. */ + exchange_establish_p1 (transport, exchange, doi, 0); + } +} + +static void +ui_delete (char *cmd) +{ + char cookies_str[ISAKMP_HDR_COOKIES_LEN * 2 + 1]; + char message_id_str[ISAKMP_HDR_MESSAGE_ID_LEN * 2 + 1]; + u_int8_t cookies[ISAKMP_HDR_COOKIES_LEN]; + u_int8_t message_id_buf[ISAKMP_HDR_MESSAGE_ID_LEN]; + u_int8_t *message_id = message_id_buf; + struct sa *sa; + + if (sscanf (cmd, "d %32s %8s", cookies_str, message_id_str) != 2) + { + log_print ("ui_delete: command \"%s\" malformed", cmd); + return; + } + + if (strcmp (message_id_str, "-") == 0) + message_id = 0; + + if (hex2raw (cookies_str, cookies, ISAKMP_HDR_COOKIES_LEN) == -1 + || (message_id && hex2raw (message_id_str, message_id_buf, + ISAKMP_HDR_MESSAGE_ID_LEN) == -1)) + { + log_print ("ui_delete: command \"%s\" has bad arguments", cmd); + return; + } + + sa = sa_lookup (cookies, message_id); + if (!sa) + { + log_print ("ui_delete: command \"%s\" found no SA", cmd); + return; + } + sa_delete (sa, 1); +} + +/* Parse the debug command found in CMD. */ +static void +ui_debug (char *cmd) +{ + int cls, level; + + if (sscanf (cmd, "d %d %d", &cls, &level) != 2) + { + log_print ("ui_debug: command \"%s\" malformed", cmd); + return; + } + log_debug_cmd (cls, level); +} + +/* Report SAs and ongoing exchanges. */ +static void +ui_report (char *cmd) +{ + sa_report (); + exchange_report (); +} + +/* + * Call the relevant command handler based on the first character of the + * line (the command). + */ +static void +ui_handle_command (char *line) +{ + /* Find out what one-letter command was sent. */ + switch (line[0]) + { + case 'c': + ui_connect (line); + break; + + case 'd': + ui_delete (line); + break; + + case 'D': + ui_debug (line); + break; + + case 'r': + ui_report (line); + break; + + default: + log_print ("ui_handle_messages: unrecognized command: '%c'", line[0]); + } +} + +/* + * A half-complex implementation of reading from a file descriptor + * line by line without resorting to stdio which apparently have + * troubles with non-blocking fifos. + */ +void +ui_handler () +{ + static char *buf = 0; + static char *p; + static size_t sz; + static size_t resid; + size_t n; + char *new_buf; + + /* If no buffer, set it up. */ + if (!buf) + { + sz = BUF_SZ; + buf = malloc (sz); + if (!buf) + { + log_print ("malloc (%d) failed", sz); + return; + } + p = buf; + resid = sz; + } + + /* If no place left in the buffer reallocate twice as large. */ + if (!resid) + { + new_buf = realloc (buf, sz * 2); + if (!new_buf) + { + log_print ("realloc (%p, %d) failed", buf, sz * 2); + free (buf); + buf = 0; + return; + } + buf = new_buf; + p = buf + sz; + resid = sz; + sz *= 2; + } + + n = read (ui_socket, p, resid); + if (n == -1) + { + log_error ("read (%d, %p, %d)", ui_socket, p, resid); + return; + } + + if (!n) + return; + resid -= n; + while (n--) + { + /* + * When we find a newline, cut off the line and feed it to the + * command processor. Then move the rest up-front. + */ + if (*p == '\n') + { + *p = '\0'; + ui_handle_command (buf); + memcpy (buf, p + 1, n); + p = buf; + resid = sz - n; + continue; + } + p++; + } +} diff --git a/sbin/isakmpd/ui.h b/sbin/isakmpd/ui.h new file mode 100644 index 00000000000..e78dd557985 --- /dev/null +++ b/sbin/isakmpd/ui.h @@ -0,0 +1,47 @@ +/* $Id: ui.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _UI_H_ +#define _UI_H_ + +#define FIFO "/var/run/isakmpd.fifo" + +extern char *ui_fifo; +extern int ui_socket; + +extern void ui_handler (void); +extern void ui_init (void); + +#endif /* _UI_H_ */ diff --git a/sbin/isakmpd/util.c b/sbin/isakmpd/util.c new file mode 100644 index 00000000000..dd27b9c2eab --- /dev/null +++ b/sbin/isakmpd/util.c @@ -0,0 +1,152 @@ +/* $Id: util.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/types.h> +#include <stdlib.h> + +#include "log.h" +#include "message.h" +#include "sysdep.h" +#include "transport.h" +#include "util.h" + +/* + * XXX These might be turned into inlines or macros, maybe even + * machine-dependent ones, for performance reasons. + */ +u_int16_t +decode_16 (u_int8_t *cp) +{ + return cp[0] << 8 | cp[1]; +} + +u_int32_t +decode_32 (u_int8_t *cp) +{ + return cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; +} + +void +encode_16 (u_int8_t *cp, u_int16_t x) +{ + *cp++ = x >> 8; + *cp = x & 0xff; +} + +void +encode_32 (u_int8_t *cp, u_int32_t x) +{ + *cp++ = x >> 24; + *cp++ = (x >> 16) & 0xff; + *cp++ = (x >> 8) & 0xff; + *cp = x & 0xff; +} + +/* Check a buffer for all zeroes. */ +int +zero_test (const u_int8_t *p, size_t sz) +{ + while (sz-- > 0) + if (*p++ != 0) + return 0; + return 1; +} + +/* + * Generate a random data, len bytes long. + */ +u_int8_t * +getrandom (u_int8_t *buf, size_t len) +{ + u_int32_t tmp = 0; + int i; + + for (i = 0; i < len; i++) + { + if (i % sizeof tmp == 0) + tmp = sysdep_random (); + + buf[i] = tmp & 0xff; + tmp >>= 8; + } + + return buf; +} + +static __inline int +hex2nibble (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +/* + * Convert hexadecimal string in S to raw binary buffer at BUF sized SZ + * bytes. Return 0 if everything is OK, -1 otherwise. + */ +int +hex2raw (char *s, u_int8_t *buf, size_t sz) +{ + char *p; + u_int8_t *bp; + int tmp; + + if (strlen (s) > sz * 2) + return -1; + for (p = s + strlen (s) - 1, bp = &buf[sz - 1]; bp >= buf; bp--) + { + *bp = 0; + if (p >= s) + { + tmp = hex2nibble (*p--); + if (tmp == -1) + return -1; + *bp = tmp; + } + if (p >= s) + { + tmp = hex2nibble (*p--); + if (tmp == -1) + return -1; + *bp |= tmp << 4; + } + } + return 0; +} diff --git a/sbin/isakmpd/util.h b/sbin/isakmpd/util.h new file mode 100644 index 00000000000..3fd89742d38 --- /dev/null +++ b/sbin/isakmpd/util.h @@ -0,0 +1,53 @@ +/* $Id: util.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include <sys/types.h> + +#define ROUNDUP_32(x) (((x) + 3) & ~4) + +struct message; + +extern u_int16_t decode_16 (u_int8_t *); +extern u_int32_t decode_32 (u_int8_t *); +extern void encode_16 (u_int8_t *, u_int16_t); +extern void encode_32 (u_int8_t *, u_int32_t); +extern u_int8_t *getrandom (u_int8_t *, size_t); +extern int hex2raw (char *, u_int8_t *, size_t); +extern int zero_test (const u_int8_t *, size_t); + +#endif /* _UTIL_H_ */ diff --git a/sbin/isakmpd/x509.c b/sbin/isakmpd/x509.c new file mode 100644 index 00000000000..9316a603255 --- /dev/null +++ b/sbin/isakmpd/x509.c @@ -0,0 +1,844 @@ +/* $Id: x509.c,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <gmp.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "conf.h" +#include "exchange.h" +#include "hash.h" +#include "ike_auth.h" +#include "sa.h" +#include "ipsec.h" +#include "log.h" +#include "asn.h" +#include "asn_useful.h" +#include "pkcs.h" +#include "x509.h" + +/* X509 Certificate Handling functions */ + +/* Validate the BER Encoding of a RDNSequence in the CERT_REQ payload */ + +int +x509_certreq_validate (u_int8_t *asn, u_int32_t len) +{ + struct norm_type name = SEQOF ("issuer", RDNSequence); + int res = 1; + + if (asn_template_clone (&name, 1) == NULL || + (asn = asn_decode_sequence (asn, len, &name)) == NULL) + { + log_print ("x509_certreq_validate: can not decode 'acceptable CA' info"); + res = 0; + } + asn_free (&name); + + return res; +} + +/* Decode the BER Encoding of a RDNSequence in the CERT_REQ payload */ + +void * +x509_certreq_decode (u_int8_t *asn, u_int32_t len) +{ + struct norm_type aca = SEQOF ("aca", RDNSequence); + struct norm_type *tmp; + struct x509_aca naca, *ret; + + if (asn_template_clone (&aca, 1) == NULL || + (asn = asn_decode_sequence (asn, len, &aca)) == NULL) + { + log_print ("x509_certreq_validate: can not decode 'acceptable CA' info"); + goto fail; + } + memset (&naca, 0, sizeof (naca)); + + tmp = asn_decompose ("aca.RelativeDistinguishedName.AttributeValueAssertion", &aca); + if (tmp == NULL) + goto fail; + x509_get_attribval (tmp, &naca.name1); + + tmp = asn_decompose ("aca.RelativeDistinguishedName[1].AttributeValueAssertion", &aca); + if (tmp != NULL) + x509_get_attribval (tmp, &naca.name2); + + asn_free (&aca); + + if ((ret = malloc (sizeof (struct x509_aca))) != NULL) + memcpy (ret, &naca, sizeof (struct x509_aca)); + else + x509_free_aca (&aca); + + return ret; + + fail: + asn_free (&aca); + return NULL; +} + +void +x509_free_aca (void *blob) +{ + struct x509_aca *aca = blob; + + if (aca->name1.type != NULL) + free (aca->name1.type); + if (aca->name1.val != NULL) + free (aca->name1.val); + + if (aca->name2.type != NULL) + free (aca->name2.type); + if (aca->name2.val != NULL) + free (aca->name2.val); +} + +/* + * Obtain a Certificate from an acceptable Certification Authority. + * XXX - this is where all the magic should happen, but yet here + * you will find nothing :-\ + */ + +int +x509_cert_obtain (struct exchange *exchange, void *data, u_int8_t **cert, + u_int32_t *certlen) +{ + struct x509_aca *aca = data; + struct ipsec_exch *ie = exchange->data; + char *certfile; + int fd, res = 0; + struct stat st; + + if (aca != NULL) + log_debug (LOG_CRYPTO, 60, "x509_cert_obtain: (%s) %s, (%s) %s", + asn_parse_objectid (asn_ids, aca->name1.type), aca->name1.val, + asn_parse_objectid (asn_ids, aca->name2.type), aca->name2.val); + + /* XXX - this needs to be changed - but how else shoud I know */ + switch (ie->ike_auth->id) + { + case IKE_AUTH_RSA_SIG: + if ((certfile = conf_get_str ("rsa_sig", "cert")) == NULL) + return 0; + break; + default: + return 0; + } + + if (stat (certfile, &st) == -1) + { + log_error ("x509_cert_obtain: failed to state %s", certfile); + return 0; + } + + *certlen = st.st_size; + + if ((fd = open (certfile, O_RDONLY)) == -1) + { + log_error ("x509_cert_obtain: failed to open %s", certfile); + return 0; + } + + if ((*cert = malloc (st.st_size)) == NULL) + { + log_print ("x509_cert_obtain: out of memory"); + res = 0; + goto done; + } + + if (read (fd, *cert, st.st_size) != st.st_size) + { + log_print ("x509_cert_obtain: cert file ended early"); + free (*cert); + res = 0; + goto done; + } + + { + /* + * XXX - assumes IPv4 here, assumes a certificate with an extension + * type of subjectAltName at the end - this can go once the saved + * certificate is only used with one host with a fixed IP address + */ + u_int8_t *id_cert, *asn, *id; + size_t id_len; + u_int32_t id_cert_len; + + /* XXX - assumes IPv4 */ + id = exchange->initiator ? exchange->id_i : exchange->id_r; + id_len = exchange->initiator ? exchange->id_i_len : exchange->id_r_len; + + /* XXX - we need our ID to set that in the cert */ + if (id != NULL) + { + /* XXX - get to address ? */ + id += 4; id_len -= 4; + + /* Get offset into data structure where the IP is saved */ + asn = *cert; + id_cert_len = asn_get_data_len (NULL, &asn, &id_cert); + asn = id_cert; + id_cert_len = asn_get_data_len (NULL, &asn, &id_cert); + id_cert += id_cert_len - 4; + memcpy (id_cert, id, 4); + } + } + + res = 1; + + done: + close (fd); + + return res; +} + +/* Retrieve the public key from a X509 Certificate */ +int +x509_cert_get_key (u_int8_t *asn, u_int32_t asnlen, void *blob) +{ + struct rsa_public_key *key = blob; + struct x509_certificate cert; + + if (!x509_decode_certificate (asn, asnlen, &cert)) + return 0; + + /* XXX - perhaps put into pkcs ? */ + mpz_init_set (key->n, cert.key.n); + mpz_init_set (key->e, cert.key.e); + + x509_free_certificate (&cert); + + return 1; +} + +/* + * Retrieve the public key from a X509 Certificate + * XXX - look at XXX below. + */ + +int +x509_cert_get_subject (u_int8_t *asn, u_int32_t asnlen, + u_int8_t **subject, u_int32_t *subjectlen) +{ + struct x509_certificate cert; + + if (!x509_decode_certificate (asn, asnlen, &cert)) + return 0; + + if (cert.extension.type == NULL || cert.extension.val == NULL) + goto fail; + + log_debug (LOG_CRYPTO, 60, "x509_cert_get_subject: Extension Type %s = %s", + cert.extension.type, + asn_parse_objectid (asn_ids, cert.extension.type)); + + if (strcmp (ASN_ID_SUBJECT_ALT_NAME, cert.extension.type)) + { + log_print ("x509_cert_get_subject: extension type != subjectAltName"); + goto fail; + } + + /* + * XXX Evil**3, due to lack of time the IP encoding of subjectAltName + * is supposed to be: 0x30 0x06 0x087 0x04 aa bb cc dd, where the IPV4 + * IP number is aa.bb.cc.dd. + */ + + if (asn_get_len (cert.extension.val) != 8 || cert.extension.val[3] != 4) + { + log_print ("x509_cert_get_subject: subjectAltName uses " + "unhandled encoding"); + goto fail; + } + + /* XXX - 4 bytes for IPV4 address */ + *subject = malloc (4); + if (*subject == NULL) + { + log_print ("x509_cert_get_subject: out of memory"); + goto fail; + } + *subjectlen = 4; + memcpy (*subject, cert.extension.val + 4, *subjectlen); + + x509_free_certificate (&cert); + + return 1; + + fail: + x509_free_certificate (&cert); + return 0; +} + +/* Initalizes the struct x509_attribval from a AtributeValueAssertion */ + +void +x509_get_attribval (struct norm_type *obj, struct x509_attribval *a) +{ + struct norm_type *tmp; + + tmp = asn_decompose ("AttributeValueAssertion.AttributeType", obj); + if (tmp != NULL && tmp->data != NULL) + a->type = strdup ((char *)tmp->data); + + tmp = asn_decompose ("AttributeValueAssertion.AttributeValue", obj); + if (tmp != NULL && tmp->data != NULL) + a->val = strdup ((char *)tmp->data); +} + +/* Set's norm_type with values from x509_attribval */ + +void +x509_set_attribval (struct norm_type *obj, struct x509_attribval *a) +{ + struct norm_type *tmp; + + tmp = asn_decompose ("AttributeValueAssertion.AttributeType", obj); + tmp->data = strdup (a->type); + tmp->len = strlen (tmp->data); + tmp = asn_decompose ("AttributeValueAssertion.AttributeValue", obj); + tmp->type = TAG_PRINTSTRING; + tmp->data = strdup (a->val); + tmp->len = strlen (tmp->data); +} + +void +x509_free_attribval (struct x509_attribval *a) +{ + if (a->type != NULL) + free (a->type); + if (a->val != NULL) + free (a->val); +} + +void +x509_free_certificate (struct x509_certificate *cert) +{ + pkcs_free_public_key (&cert->key); + if (cert->signaturetype != NULL) + free (cert->signaturetype); + if (cert->start != NULL) + free (cert->start); + if (cert->end != NULL) + free (cert->end); + + x509_free_attribval (&cert->issuer1); + x509_free_attribval (&cert->issuer2); + x509_free_attribval (&cert->subject1); + x509_free_attribval (&cert->subject2); + x509_free_attribval (&cert->extension); +} + +int +x509_decode_certificate (u_int8_t *asn, u_int32_t asnlen, + struct x509_certificate *rcert) +{ + struct norm_type cert = SEQ ("cert", Certificate); + struct norm_type *tmp; + u_int8_t *data; + u_int32_t datalen; + + /* Get access to the inner Certificate */ + if (!x509_validate_signed (asn, asnlen, NULL, &data, &datalen)) + return 0; + + memset (rcert, 0, sizeof (*rcert)); + + if (asn_template_clone (&cert, 1) == NULL || + asn_decode_sequence (data, datalen, &cert) == NULL) + goto fail; + + tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert); + if (!pkcs_public_key_from_asn (&rcert->key, tmp->data + 1, tmp->len - 1)) + goto fail; + + tmp = asn_decompose ("cert.version", &cert); + rcert->version = mpz_get_ui (tmp->data); + tmp = asn_decompose ("cert.serialNumber", &cert); + rcert->serialnumber = mpz_get_ui (tmp->data); + tmp = asn_decompose ("cert.signature.algorithm", &cert); + rcert->signaturetype = strdup ((char *)tmp->data); + + tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName." + "AttributeValueAssertion", &cert); + x509_get_attribval (tmp, &rcert->issuer1); + tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName[1]." + "AttributeValueAssertion", &cert); + if (tmp != NULL) + x509_get_attribval (tmp, &rcert->issuer2); + else + rcert->issuer2.type = NULL; + + tmp = asn_decompose ("cert.subject.RelativeDistinguishedName." + "AttributeValueAssertion", &cert); + x509_get_attribval (tmp, &rcert->subject1); + tmp = asn_decompose ("cert.subject.RelativeDistinguishedName[1]." + "AttributeValueAssertion", &cert); + if (tmp != NULL) + x509_get_attribval (tmp, &rcert->subject2); + else + rcert->subject2.type = NULL; + + tmp = asn_decompose ("cert.validity.notBefore", &cert); + rcert->start = strdup ((char *)tmp->data); + tmp = asn_decompose ("cert.validity.notAfter", &cert); + rcert->end = strdup ((char *)tmp->data); + + /* For x509v3 there might be an extension, try to decode it */ + tmp = asn_decompose ("cert.extension", &cert); + if (tmp && tmp->data && rcert->version == 2) + x509_decode_cert_extension (tmp->data, tmp->len, rcert); + + asn_free (&cert); + return 1; + + fail: + x509_free_certificate (rcert); + asn_free (&cert); + return 0; +} + +int +x509_encode_certificate (struct x509_certificate *rcert, + u_int8_t **asn, u_int32_t *asnlen) +{ + struct norm_type cert = SEQ ("cert", Certificate); + struct norm_type *tmp; + u_int8_t *data, *new_buf; + mpz_t num; + + if (asn_template_clone (&cert, 1) == NULL) + goto fail; + + if (rcert->extension.type != NULL && rcert->extension.val != NULL) + { + u_int8_t *asn; + u_int32_t asnlen; + + tmp = asn_decompose ("cert.extension", &cert); + if (x509_encode_cert_extension (rcert, &asn, &asnlen)) + { + tmp->data = asn; + tmp->len = asnlen; + } + } + + tmp = asn_decompose ("cert.subjectPublicKeyInfo.algorithm.parameters", + &cert); + tmp->type = TAG_NULL; + tmp = asn_decompose ("cert.subjectPublicKeyInfo.algorithm.algorithm", + &cert); + tmp->data = strdup (ASN_ID_RSAENCRYPTION); + tmp->len = strlen (tmp->data); + + tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert); + data = pkcs_public_key_to_asn (&rcert->key); + if (data == NULL) + goto fail; + + /* This is a BIT STRING add 0 octet for padding */ + tmp->len = asn_get_len (data); + new_buf = realloc (data, tmp->len + 1); + if (new_buf == NULL) + { + free (data); + goto fail; + } + data = new_buf; + memmove (data + 1, data, tmp->len); + data[0] = 0; + tmp->data = data; + tmp->len++; + + mpz_init (num); + tmp = asn_decompose ("cert.version", &cert); + mpz_set_ui (num, rcert->version); + if (!pkcs_mpz_to_norm_type (tmp, num)) + { + mpz_clear (num); + goto fail; + } + + tmp = asn_decompose ("cert.serialNumber", &cert); + mpz_set_ui (num, rcert->serialnumber); + if (!pkcs_mpz_to_norm_type (tmp, num)) + { + mpz_clear (num); + goto fail; + } + mpz_clear (num); + + tmp = asn_decompose ("cert.signature.parameters", &cert); + tmp->type = TAG_NULL; + tmp = asn_decompose ("cert.signature.algorithm", &cert); + tmp->data = strdup (rcert->signaturetype); + tmp->len = strlen ((char *)tmp->data); + + tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName." + "AttributeValueAssertion", &cert); + x509_set_attribval (tmp, &rcert->issuer1); + tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName[1]." + "AttributeValueAssertion", &cert); + x509_set_attribval (tmp, &rcert->issuer2); + + tmp = asn_decompose ("cert.subject.RelativeDistinguishedName." + "AttributeValueAssertion", &cert); + x509_set_attribval (tmp, &rcert->subject1); + tmp = asn_decompose ("cert.subject.RelativeDistinguishedName[1]." + "AttributeValueAssertion", &cert); + x509_set_attribval (tmp, &rcert->subject2); + + tmp = asn_decompose ("cert.validity.notBefore", &cert); + tmp->data = strdup (rcert->start); + tmp->len = strlen ((char *)tmp->data); + + tmp = asn_decompose ("cert.validity.notAfter", &cert); + tmp->data = strdup (rcert->end); + tmp->len = strlen ((char *)tmp->data); + + *asn = asn_encode_sequence (&cert, NULL); + if (*asn == NULL) + goto fail; + + *asnlen = asn_get_len (*asn); + + asn_free (&cert); + return 1; + + fail: + asn_free (&cert); + return 0; +} + +/* + * Decode an Extension to a X509 certificate. + * XXX - We ignore the critical boolean + */ + +int +x509_decode_cert_extension (u_int8_t *asn, u_int32_t asnlen, + struct x509_certificate *cert) +{ + struct norm_type *tmp; + struct norm_type ex = SEQOF ("ex", Extensions); + + /* Implicit tagging for extension */ + ex.class = ADD_EXP (3, UNIVERSAL); + + if (asn_template_clone (&ex, 1) == NULL || + asn_decode_sequence (asn, asnlen, &ex) == NULL) + { + asn_free (&ex); + return 0; + } + + tmp = asn_decompose ("ex.extension.extnValue", &ex); + if (!tmp || tmp->data == NULL || asn_get_len (tmp->data) != tmp->len) + goto fail; + cert->extension.val = malloc (tmp->len); + if (cert->extension.val == 0) + goto fail; + memcpy (cert->extension.val, tmp->data, tmp->len); + + tmp = asn_decompose ("ex.extension.extnId", &ex); + if (!tmp || tmp->data == NULL) + goto fail; + cert->extension.type = strdup (tmp->data); + if (cert->extension.type == NULL) + { + free (cert->extension.val); + cert->extension.val = NULL; + goto fail; + } + + asn_free (&ex); + return 1; + + fail: + asn_free (&ex); + return 0; +} + +/* + * Encode a Cert Extension - XXX - only one extension per certificate + * XXX - We tag everything as critical + */ + +int +x509_encode_cert_extension (struct x509_certificate *cert, + u_int8_t **asn, u_int32_t *asnlen) +{ + struct norm_type ex = SEQ ("ex", Extensions); + struct norm_type *tmp; + ex.class = ADD_EXP (3, UNIVERSAL); + + if (asn_template_clone (&ex ,1) == NULL) + goto fail; + + tmp = asn_decompose ("ex.extension.extnId", &ex); + tmp->data = strdup (cert->extension.type); + tmp->len = strlen (tmp->data); + + /* XXX - we mark every extension as critical */ + tmp = asn_decompose ("ex.extension.critical", &ex); + if ((tmp->data = malloc (1)) == NULL) + goto fail; + *(u_int8_t *)tmp->data = 0xFF; + tmp->len = 1; + + tmp = asn_decompose ("ex.extension.extnValue", &ex); + if ((tmp->data = malloc (asn_get_len (cert->extension.val))) == NULL) + goto fail; + tmp->len = asn_get_len (cert->extension.val); + memcpy (tmp->data, cert->extension.val, tmp->len); + + *asn = asn_encode_sequence (&ex, NULL); + if (*asn == NULL) + goto fail; + + *asnlen = asn_get_len (*asn); + + asn_free (&ex); + return 1; + fail: + asn_free (&ex); + return 0; +} + +/* + * Checks the signature on an ASN.1 Signed Type. If the passed Key is + * NULL we just unwrap the inner object and return it. + */ + +int +x509_validate_signed (u_int8_t *asn, u_int32_t asnlen, + struct rsa_public_key *key, u_int8_t **data, + u_int32_t *datalen) +{ + struct norm_type sig = SEQ("signed", Signed); + struct norm_type digest = SEQ("digest", DigestInfo); + struct norm_type *tmp; + struct hash *hash = NULL; + int res; + u_int8_t *dec; + u_int16_t declen; + + if (asn_template_clone (&sig, 1) == NULL) + /* Failed, probably memory allocation, free what we got anyway */ + goto fail; + + if (asn_decode_sequence (asn, asnlen, &sig) == NULL) + { + log_print ("x509_validate_signed: input data could not be decoded"); + goto fail; + } + + tmp = asn_decompose ("signed.algorithm.algorithm", &sig); + + if (!strcmp ((char *)tmp->data, ASN_ID_MD5WITHRSAENC)) + { + hash = hash_get (HASH_MD5); + } + else + { + char *id = asn_parse_objectid (asn_ids, tmp->data); + log_print ("x509_validate_signed: can not handle SigType %s", + id == NULL ? tmp->data : id); + goto fail; + } + + if (hash == NULL) + goto fail; + + tmp = asn_decompose ("signed.data", &sig); + /* Hash the data */ + hash->Init (hash->ctx); + hash->Update (hash->ctx, tmp->data, tmp->len); + hash->Final (hash->digest, hash->ctx); + + *data = tmp->data; + *datalen = tmp->len; + + /* Used to unwrap the SIGNED object around the Certificate */ + if (key == NULL) + { + asn_free (&sig); + return 1; + } + + tmp = asn_decompose ("signed.encrypted", &sig); + /* + * tmp->data is a BIT STRING, the first octet in the BIT STRING gives + * the padding bits at the end. Per definition there are no padding + * bits at the end in this case, so just skip it. + */ + if (!pkcs_rsa_decrypt (PKCS_PRIVATE, key->n, key->e, tmp->data + 1, + &dec, &declen)) + goto fail; + + if (asn_template_clone (&digest, 1) == NULL || + asn_decode_sequence (dec, declen, &digest) == NULL) + { + asn_free (&digest); + goto fail; + } + tmp = asn_decompose ("digest.digestAlgorithm.algorithm", &digest); + if (strcmp (ASN_ID_MD5, (char *)tmp->data)) + { + log_print ("x509_validate_signed: DigestAlgorithm is not MD5"); + res = 0; + } + else + { + tmp = asn_decompose ("digest.digest", &digest); + if (tmp->len != hash->hashsize || + memcmp (tmp->data, hash->digest, tmp->len)) + { + log_print ("x509_validate_signed: Digest does not match Data"); + res = 0; + } + else + res = 1; + } + + asn_free (&digest); + asn_free (&sig); + return res; + + fail: + asn_free (&sig); + return 0; +} + +/* + * Create an ASN Signed Structure from the data passed in data + * and return the result in asn. + * At the moment the used hash is MD5, this is the only common + * hash between us and X509. + */ + +int +x509_create_signed (u_int8_t *data, u_int32_t datalen, + struct rsa_private_key *key, u_int8_t **asn, + u_int32_t *asnlen) +{ + struct norm_type digest = SEQ ("digest", DigestInfo); + struct norm_type sig = SEQ ("signed", Signed); + struct norm_type *tmp; + struct hash *hash; + u_int8_t *diginfo, *enc; + u_int32_t enclen; + int res = 0; + + /* Hash the Data */ + hash = hash_get (HASH_MD5); + hash->Init (hash->ctx); + hash->Update (hash->ctx, data, datalen); + hash->Final (hash->digest, hash->ctx); + + if (asn_template_clone (&digest, 1) == NULL) + goto fail; + + tmp = asn_decompose ("digest.digest", &digest); + tmp->len = hash->hashsize; + tmp->data = malloc (hash->hashsize); + if (tmp->data == NULL) + goto fail; + memcpy (tmp->data, hash->digest, hash->hashsize); + + tmp = asn_decompose ("digest.digestAlgorithm.parameters", &digest); + tmp->type = TAG_NULL; + tmp = asn_decompose ("digest.digestAlgorithm.algorithm", &digest); + tmp->data = strdup (ASN_ID_MD5); + tmp->len = strlen (tmp->data); + + /* ASN encode Digest Information */ + if ((diginfo = asn_encode_sequence (&digest, NULL)) == NULL) + goto fail; + + /* Encrypt the Digest Info with Private Key */ + res = pkcs_rsa_encrypt (PKCS_PRIVATE, key->n, key->d, diginfo, + asn_get_len (diginfo), &enc, &enclen); + free (diginfo); + if (!res) + goto fail; + res = 0; + + if (asn_template_clone (&sig, 1) == NULL) + goto fail2; + + tmp = asn_decompose ("signed.algorithm.parameters", &sig); + tmp->type = TAG_NULL; + tmp = asn_decompose ("signed.algorithm.algorithm", &sig); + tmp->data = strdup (ASN_ID_MD5WITHRSAENC); + tmp->len = strlen (tmp->data); + + /* The type is BITSTING, i.e. first octet need to be zero */ + tmp = asn_decompose ("signed.encrypted", &sig); + tmp->data = malloc (enclen + 1); + if (tmp->data == NULL) + { + free (enc); + goto fail2; + } + tmp->len = enclen + 1; + memcpy (tmp->data + 1, enc, enclen); + *(char *)tmp->data = 0; + free (enc); + + tmp = asn_decompose ("signed.data", &sig); + tmp->data = data; + tmp->len = datalen; + + *asn = asn_encode_sequence (&sig, NULL); + if (*asn == NULL) + goto fail2; + *asnlen = asn_get_len (*asn); + + /* This is the data we have been given, we can not free it in asn_free */ + tmp->data = NULL; + res = 1; /* Successfull */ + fail2: + asn_free (&sig); + fail: + asn_free (&digest); + return res; +} diff --git a/sbin/isakmpd/x509.h b/sbin/isakmpd/x509.h new file mode 100644 index 00000000000..577f858749a --- /dev/null +++ b/sbin/isakmpd/x509.h @@ -0,0 +1,98 @@ +/* $Id: x509.h,v 1.1 1998/11/15 00:03:49 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifndef _X509_H_ +#define _X509_H_ + +#include "pkcs.h" /* for struct rsa_public_key */ + +struct x509_attribval { + char *type; + char *val; +}; + +/* + * The acceptable certification authority + * XXX we only support two names at the moment, as of ASN this can + * be dynamic but we dont care for now. + */ + +struct x509_aca { + struct x509_attribval name1; + struct x509_attribval name2; +}; + +struct exchange; + +struct x509_certificate { + u_int32_t version; + u_int32_t serialnumber; + char *signaturetype; + struct x509_attribval issuer1; /* At the moment Country */ + struct x509_attribval issuer2; /* At the moment Organization */ + struct x509_attribval subject1; /* At the moment Country */ + struct x509_attribval subject2; /* At the moment Organization */ + struct x509_attribval extension; /* Raw Extension */ + char *start; /* Certificate Validity Start and End */ + char *end; + struct rsa_public_key key; +}; + +int x509_certreq_validate (u_int8_t *, u_int32_t); +void *x509_certreq_decode (u_int8_t *, u_int32_t); +void x509_free_aca (void *); +int x509_cert_obtain (struct exchange *, void *, u_int8_t **, u_int32_t *); +int x509_cert_get_key (u_int8_t *, u_int32_t, void *); +int x509_cert_get_subject (u_int8_t *, u_int32_t, u_int8_t **, u_int32_t *); + +void x509_get_attribval (struct norm_type *, struct x509_attribval *); +void x509_set_attribval (struct norm_type *, struct x509_attribval *); +void x509_free_attrbival (struct x509_attribval *); + +int x509_validate_signed (u_int8_t *, u_int32_t, struct rsa_public_key *, + u_int8_t **, u_int32_t *); +int x509_create_signed (u_int8_t *, u_int32_t, struct rsa_private_key *, + u_int8_t **, u_int32_t *); +int x509_decode_certificate (u_int8_t *, u_int32_t, struct x509_certificate *); +int x509_encode_certificate (struct x509_certificate *, u_int8_t **, + u_int32_t *); +void x509_free_certificate (struct x509_certificate *); + +int x509_decode_cert_extension (u_int8_t *, u_int32_t, + struct x509_certificate *); +int x509_encode_cert_extension (struct x509_certificate *, u_int8_t **, + u_int32_t *); + +#endif /* _X509_H_ */ |