xref: /openbsd/sbin/isakmpd/DESIGN-NOTES (revision 5678a57a)
1*5678a57aShshoexer$OpenBSD: DESIGN-NOTES,v 1.25 2006/06/02 19:35:55 hshoexer Exp $
29d4c2f25Sniklas$EOM: DESIGN-NOTES,v 1.48 1999/08/12 22:34:25 niklas Exp $
32040585eSniklas
42040585eSniklasGeneral coding conventions
52040585eSniklas--------------------------
62040585eSniklasGNU indentation, Max 80 characters per line, KNF comments, mem* instead of b*,
7002a8f41SniklasBSD copyright, one header per module specifying the API.
82040585eSniklasMultiple inclusion protection like this:
92040585eSniklas
102040585eSniklas#ifndef _HEADERNAME_H_
112040585eSniklas#define _HEADERNAME_H_
122040585eSniklas
132040585eSniklas... Here comes the bulk of the header ...
142040585eSniklas
152040585eSniklas#endif /* _HEADERNAME_H_ */
162040585eSniklas
172b81057dSniklasStart all files with RCS ID tags.
182040585eSniklas
192040585eSniklasGCC -Wall clean, ANSI prototypes.  System dependent facilities should be
201bd1ea20Sniklasnamed sysdep_* and be placed in sysdep.c.  Every C file should include
211bd1ea20Sniklassysdep.h as the first isakmpd include file.  Primary target systems are OpenBSD
222040585eSniklasand Linux, but porting to Microsoft Windows variants should not be made
232040585eSniklasoverly difficult.
242040585eSniklas
25c6bfb327SniklasNote places which need reconsiderations with comments starting with the
262040585eSniklasstring "XXX", e.g.
272040585eSniklas
282040585eSniklas/* XXX Not implemented yet.  */
292040585eSniklas
302040585eSniklasTOC
312040585eSniklas---
322040585eSniklasapp.c		Application support.
334a3d04dbShoattribute.c	Attribute handling.
342040585eSniklascert.c		Dispatching certificate related functions to the according
352040585eSniklas		module based on the encoding.
362040585eSniklasconf.c		Interface to isakmpd configuration.
37b393bef8Sniklasconnection.c	Handle the high-level connection concept.
381f98d23fSniklasconstants.c	Value to name map of constants.
392040585eSniklascookie.c	Cookie generation.
402040585eSniklascrypto.c	Generic cryptography.
412040585eSniklasdh.c		Diffie-Hellman exchange logic.
424a3d04dbShodnssec.c	IKE authentication using signed DNS KEY RRs.
432040585eSniklasdoi.c		Generic handling of different DOIs.
442040585eSniklasexchange.c	Exchange state machinery.
452040585eSniklasexchange_num.cst
46*5678a57aShshoexer		Some constants used for exchange scripts.
472040585eSniklasfield.c		Generic handling of fields.
482040585eSniklasgenconstants.sh
492040585eSniklas		Generate constant files from .cst source.
502040585eSniklasgenfields.sh	Generate field description files from .fld source.
51*5678a57aShshoexergmp_util.c	Utilities to ease interfacing to GMP.
522040585eSniklashash.c		Generic hash handling.
532040585eSniklasif.c		Network interface details.
54c8b8b55eSniklasike_aggressive.c
551f98d23fSniklas		IKE's aggressive mode exchange logic.
564a3d04dbShoike_auth.c	IKE authentication method abstraction.
572040585eSniklasike_main_mode.c	IKE's main mode exchange logic.
58c8b8b55eSniklasike_phase_1.c	Common parts IKE's main & aggressive modes' exchange logic.
592040585eSniklasike_quick_mode.c
602040585eSniklas		IKE's quick mode logic.
612040585eSniklasinit.c		Initialization of all modules (might be autogenned in the
622040585eSniklas		future).
6361d575f0Sangelosipsec.c		The IPsec DOI.
6461d575f0Sangelosipsec_fld.fld	Description of IPsec DOI-specific packet layouts.
6561d575f0Sangelosipsec_num.cst	Constants defined by the IPsec DOI.
662040585eSniklasisakmp_doi.c	The ISAKMP pseudo-DOI.
672040585eSniklasisakmp_fld.fld	Generic packet layout.
682040585eSniklasisakmp_num.cst	ISAKMP constants.
692040585eSniklasisakmpd.c	Main loop.
704a3d04dbShokey.c		Generic key handling.
7146e419f0Sholibcrypto.c	Initialize crypto (OpenSSL).
722040585eSniklaslog.c		Logging of exceptional or informational messages.
732040585eSniklasmath_2n.c	Polynomial math.
742040585eSniklasmath_ec2n.c	Elliptic curve math.
752040585eSniklasmath_group.c	Group math.
762040585eSniklasmessage.c	Generic message handling.
7761d575f0Sangelospf_key_v2.c	Interface with PF_KEY sockets (for use with IPsec).
78556701c4Sniklaspolicy.c	Keynote glue.
792040585eSniklasprf.c		Pseudo random functions.
802040585eSniklassa.c		Handling of Security Associations (SAs).
81c6bfb327Sniklassysdep/*/sysdep.c
82c6bfb327Sniklas		System dependent stuff.
832040585eSniklastimer.c		Timed events.
842040585eSniklastransport.c	Generic transport handling.
852040585eSniklasudp.c		The UDP transport.
862040585eSniklasui.c		The "User Interface", i.e. the FIFO command handler.
872040585eSniklasutil.c		Miscellaneous utility functions.
882040585eSniklasx509.c		Encoding/Decoding X509 Certificates and related structures.
892040585eSniklas
902040585eSniklasCentral datatypes
912040585eSniklas-----------------
922040585eSniklas
93b393bef8Sniklasstruct connection	Persistent connections.
942040585eSniklasstruct constant_map	A map from constants to their ASCII names.
952040585eSniklasstruct crypto_xf	A crypto class
962040585eSniklasstruct doi		The DOI function switch
972040585eSniklasstruct event		An event that is to happen at some point in time.
982040585eSniklasstruct exchange		A description of an exchange while it is performed.
992040585eSniklasstruct field		A description of an ISAKMP field.
1002040585eSniklasstruct group		A class abstracting out Oakley group operations
1012040585eSniklasstruct hash		A hashing class
10261d575f0Sangelosstruct ipsec_exch	IPsec-specific exchange fields.
10361d575f0Sangelosstruct ipsec_proto	IPsec-specific protocol attributes.
10461d575f0Sangelosstruct ipsec_sa		IPsec-specific SA stuff.
1052040585eSniklasstruct message		A generic ISAKMP message.
1062040585eSniklasstruct payload		A "fat" payload reference pointing into message buffers
1072040585eSniklasstruct prf		A pseudo random function class
1082040585eSniklasstruct proto		Per-protocol attributes.
1092040585eSniklasstruct post_send	Post-send function chain node.
1102040585eSniklasstruct sa		A security association.
1112040585eSniklasstruct transport	An ISAKMP transport, i.e. a channel where ISAKMP
1122040585eSniklas			messages are passed (not necessarily connection-
1132040585eSniklas			oriented).  This is an abstract class, serving as
1142040585eSniklas			a superclass to the different specific transports.
1152040585eSniklas
1162040585eSniklasSAs & exchanges
1172040585eSniklas---------------
1182040585eSniklas
1192040585eSniklasstruct exchange		Have all fields belonging to a simple exchange
1202040585eSniklas			+ a list of all the SAs being negotiated.
1212040585eSniklas			Short-lived.
1222040585eSniklasstruct sa		Only hold SA-specific stuff.  Lives longer.
1232040585eSniklas
1242040585eSniklasIn order to recognize exchanges and SAs it is good to know what constitutes
1252040585eSniklastheir identities:
1262040585eSniklas
1272040585eSniklasPhase 1 exchange	Cookie pair (apart from the first message of course,
1282040585eSniklas			where the responder cookie is zero.
1292040585eSniklas
1302040585eSniklasISAKMP SA		Cookie pair.  I.e. there exists a one-to-one
1312040585eSniklas			mapping to the negotiation in this case.
1322040585eSniklas
1332040585eSniklasPhase 2 exchange	Cookie pair + message ID.
1342040585eSniklas
1352040585eSniklasGeneric SA		Cookie pair + message ID + SPI.
1362040585eSniklas
1372040585eSniklasHowever it would be really nice to have a name of any SA that is natural
1381f98d23fSniklasto use for human beings, for things like deleting SAs manually.  The simplest
1392040585eSniklasID would be the struct sa address.  Another idea would be some kind of sequence
1402b81057dSniklasnumber, either global or per-destination.  Right now I have introduced a name
1412b81057dSniklasfor SAs, non-unique, that binds together SAs and their configuration
1422b81057dSniklasparameters.  This means both manual exchange runs and rekeying are simpler.
143c6bfb327SniklasBoth struct exchange and struct sa does hold a reference count, but this is
144c6bfb327Sniklasnot entirely like a reference count in the traditional meaning where
145c6bfb327Sniklasevery reference gets counted.  Perhaps it will be in the future, but for now
146c6bfb327Sniklaswe increment the count at allocation time and at times we schedule events
147*5678a57aShshoexerthat might happen sometime in the future where we will need the structure.
148*5678a57aShshoexerThese events then release its reference when done.  This way intermediate
149c6bfb327Sniklasdeallocation of these structures are OK.
1502040585eSniklas
1512040585eSniklasThe basic idea of control flow
1522040585eSniklas------------------------------
1532040585eSniklas
1542040585eSniklasThe main loop just waits for events of any kind.  Supposedly a message
1552040585eSniklascomes in, then the daemon looks to see if the cookies describes an
1562040585eSniklasexisting ISAKMP SA, if they don't and the rcookie is zero, it triggers a
1572040585eSniklassetup of a new ISAKMP SA.  An exhaustive validation phase of the message
1582040585eSniklasis gone through at this stage.  If anything goes wrong, we drop the packet
1592040585eSniklasand probably send some notification back.  After the SA is found we try to
1602040585eSniklaslocate the exchange object and advance its state, else we try to create a
1612040585eSniklasnew exchange.
1622040585eSniklas
1632040585eSniklasNeed exchanges be an abstraction visible in the code?  If so an exchange is
1642040585eSniklasroughly a very simple FSM (only timeouts and retransmissions are events that
1652040585eSniklasdoes not just advance the state through a sequential single path).  The
1662040585eSniklasinformational exchange is such a special case, I am not sure it's interesting
1672040585eSniklasto treat as an exchange in the logic of the implementation.  The only reason
1682040585eSniklasto do so would be to keep the implementation tightly coupled to the
169c6bfb327Sniklasspecification for ease of understanding.  As the code looks now, exchanges
170c6bfb327Sniklas*are* an abstraction in the code, and it has proven to be a rather nice
171c6bfb327Sniklasway of having things.
1722040585eSniklas
1732040585eSniklasWhen the exchange has been found the exchange engine "runs" a script which
174c6bfb327Sniklassteps forward for each incoming message, and on each reply to them.
1752040585eSniklas
1762040585eSniklasPayload parsing details
1772040585eSniklas-----------------------
1782040585eSniklas
1792040585eSniklasAfter the generic header has been validated, we do a generic payload
1802040585eSniklasparsing pass over the message and sort out the payloads into buckets indexed
1812040585eSniklasby the payload type.  Note that proposals and transforms are part of the SA
1822040585eSniklaspayloads.  We then pass over them once more validating each payload
1832040585eSniklasin numeric payload type order.  This makes SA payloads come naturally first.
1842040585eSniklas
1852040585eSniklasMessages
1862040585eSniklas--------
1872040585eSniklas
1882040585eSniklasI am not sure there is any use in sharing the message structure for both
1892040585eSniklasincoming and outgoing messages but I do it anyhow.  Specifically there are
1902040585eSniklascertain fields which only makes sense in one direction.  Incoming messages
1912040585eSniklasonly use one segment in the iovec vector, while outgoing has one segment per
1922040585eSniklaspayload as well as one for the ISAKMP header.  The iovec vector is
1932040585eSniklasreallocated for each payload added, maybe we should do it in chunks of a
1942040585eSniklasnumber of payloads instead, like 10 or so.
1952040585eSniklas
1962040585eSniklasDesign "errors"
1972040585eSniklas---------------
1982040585eSniklas
1992040585eSniklasCurrently there are two "errors" in our design.  The first one is that the
20061d575f0Sangeloscoupling between the IPsec DOI and IKE is tight.  It should be separated by
2012040585eSniklasa clean interface letting other key exchange models fit in instead of IKE.
2022040585eSniklasThe second problem is that we need a protocol-specific opaque SA part
20361d575f0Sangelosin the DOI specific one.  Now both IPsec ESP attributes takes place even
2042040585eSniklasin ISAKMP SA structures.
2052040585eSniklas
2062040585eSniklasUser control
2072040585eSniklas------------
2082040585eSniklas
2092040585eSniklasIn order to control the daemon you send commands through a FIFO called
2102040585eSniklasisakmpd.fifo.  The commands are one-letter codes followed by arguments.
2114a3d04dbShoFor now, eleven such commands are implemented:
2122040585eSniklas
213b393bef8Sniklasc	connect		Establish a connection with a peer
2144a3d04dbShoC	configure	Add or remove configuration entries
215b393bef8Sniklasd	delete		Delete an SA given cookies and message-IDs
2161f98d23fSniklasD	debug		Change logging level for a debug class
2174a3d04dbShop	packet capture	Enable/disable packet capture feature
2182040585eSniklasr	report		Report status information of the daemon
219b393bef8Sniklast	teardown	Teardown a connection
2200c6a08f2SangelosQ       quit            Quit the isakmpd process
2210c6a08f2SangelosR       reinitialize    Reinitialize isakmpd (re-read configuration file)
2220c6a08f2SangelosS       report SA       Report SA information to file /var/run/isakmp_sa
2230c6a08f2SangelosT       teardown all    Teardown all Phase 2 connections
2242040585eSniklas
2252040585eSniklasFor example you can do:
2262040585eSniklas
227fc127296Sniklasc ISAKMP-peer
2282040585eSniklas
2292040585eSniklasIn order to delete an SA you use the 'd' command.  However this is not yet
2302040585eSniklassupported.
2312040585eSniklas
2322040585eSniklasTo alter the level of debugging in the "LOG_MISC" logging class to 99 you do:
2332040585eSniklas
2342040585eSniklasD 0 99
2352040585eSniklas
2362040585eSniklasThe report command is just an "r", and results in a list of active exchanges
2372040585eSniklasand security associations.
2382040585eSniklas
239f8f1e192SniklasThe "C" command takes 3 subcommands: set, rm and rms, for adding and removing
240f8f1e192Sniklasentries + remove complete sections respectively.  Examples:
241f8f1e192Sniklas
242f8f1e192SniklasC set [Net-A]:Address=192.168.0.0
243f8f1e192SniklasC rm [Net-A]:Address
244f8f1e192SniklasC rms [Net-A]
245f8f1e192Sniklas
246f8f1e192SniklasAll these commands are atomic, i.e. they are not collected into larger
247f8f1e192Sniklastransactions, which there should be a way to do, but currently isn't.
248f8f1e192Sniklas
2494a3d04dbShoThe FIFO UI is also described in the isakmpd(8) man page.
2502b81057dSniklas
2511f98d23fSniklasIn addition to giving commands over the FIFO, you may send signals to the
2521f98d23fSniklasdaemon. Currently two such signals are implemented:
2531f98d23fSniklas
2541f98d23fSniklasSIGHUP 	  Re-initialize isakmpd (not fully implemented yet)
2551f98d23fSniklasSIGUSR1   Generate a report, much as the "r" FIFO command.
2561f98d23fSniklas
2571f98d23fSniklasFor example, to generate a report, you do:
2581f98d23fSniklas
2591f98d23fSniklasunix# kill -USR1 <PID of isakmpd process>
2601f98d23fSniklas
2612040585eSniklasThe constant descriptions
2622040585eSniklas-------------------------
2632040585eSniklas
2642040585eSniklasWe have invented a simple constant description language, for the sake
2652040585eSniklasof easily getting textual representations of manifest constants.
2662040585eSniklasThe syntax is best described by an example:
2672040585eSniklas
2682040585eSniklasGROUP
2692040585eSniklas  CONSTANT_A		1
2702040585eSniklas  CONSTANT_B		2
2712040585eSniklas.
2722040585eSniklas
2732040585eSniklasThis defines a constant map "group" with the following two defines:
2742040585eSniklas
2752040585eSniklas#define GROUP_CONSTANT_A 1
2762040585eSniklas#define GROUP_CONSTANT_B 2
2772040585eSniklas
2782040585eSniklasWe can now get the textual representation by:
2792040585eSniklas
2802040585eSniklas  cp = constant_name (group, foo);
2812040585eSniklas
2822040585eSniklasHere foo is an integer with either of the two constants as a value.
2832040585eSniklas
2842040585eSniklasThe field descriptions
2852040585eSniklas----------------------
2862040585eSniklas
2872040585eSniklasThere is language for describing header and payload layouts too,
2882040585eSniklassimilar to the constant descriptions.  Here too I just show an example:
2892040585eSniklas
2902040585eSniklasRECORD_A
2912040585eSniklas  FIELD_A	raw 4
2922040585eSniklas  FIELD_B	num 2
2932040585eSniklas  FIELD_C	mask 1		group_c_cst
2942040585eSniklas  FIELD_D	ign 1
2952040585eSniklas  FIELD_E	cst 2		group_e1_cst,group_e2_cst
2962040585eSniklas.
2972040585eSniklas
2982040585eSniklasRECORD_B : RECORD_A
2992040585eSniklas  FIELD_F	raw
3002040585eSniklas.
3012040585eSniklas
3022040585eSniklasThis creates some utility constants like RECORD_A_SZ, RECORD_A_FIELD_A_LEN,
3032040585eSniklasRECORD_A_FIELD_A_OFF, RECORD_A_FIELD_B_LEN etc.  The *_OFF contains the
304e4d25771Stoddoctet offset into the record and the *_LEN constants are the lengths.
3052040585eSniklasThe type fields can be: raw, num, mask, ign & cst.  Raw are used for
3062040585eSniklasoctet buffers, num for (unsigned) numbers of 1, 2 or 4 octet's length
3072040585eSniklasin network byteorder, mask is a bitmask where the bit values have symbols
3082040585eSniklascoupled to them via the constant maps given after the length in octets
3092040585eSniklas(also 1, 2 or 4).  Ign is just a filler type, ot padding and lastly cst
3102040585eSniklasdenotes constants whose values can be found in the given constant map(s).
3112040585eSniklasThe last field in a record can be a raw, without a length, then just an
3122040585eSniklas_OFF symbol will be generated.  You can offset the first symbol to the
3132040585eSniklassize of another record, like is done above for RECORD_B, i.e. in that
3142040585eSniklascase RECORD_A_SZ == RECORD_B_FIELD_F_OFF.  All this data are collected
3152040585eSniklasin struct field arrays which makes it possible to symbolically print out
3162040585eSniklasentire payloads in readable form via field_dump_payload.
3172040585eSniklas
318f8f1e192SniklasConfiguration
319f8f1e192Sniklas-------------
320f8f1e192Sniklas
321f8f1e192SniklasInternally isakmpd uses a section-tag-value triplet database for
322f8f1e192Sniklasconfiguration.  Currently this happen to map really well to the
323f8f1e192Sniklasconfiguration file format, which on the other hand does not map
324f8f1e192Sniklasequally well to humans.  It is envisioned that the configuration
325f8f1e192Sniklasdatabase should be dynamically modifiable, and through a lot of
326*5678a57aShshoexerdifferent mechanisms.  Therefore we have designed an API for this
327f8f1e192Sniklaspurpose.
328f8f1e192Sniklas
329f8f1e192Sniklasint conf_begin ();
330f8f1e192Sniklasint conf_set (int transaction, char *section, char *tag, char *value,
331f8f1e192Sniklas	      int override);
332f8f1e192Sniklasint conf_remove (int transaction, char *section, char *tag);
333f8f1e192Sniklasint conf_remove_section (int transaction, char *section);
334f8f1e192Sniklasint conf_end (int transaction, int commit);
335f8f1e192Sniklas
336f8f1e192SniklasThe caller will always be responsible for the memory management of the
337f8f1e192Sniklaspassed strings, conf_set will copy the values, and not use the original
338f8f1e192Sniklasstrings after it has returned.  Return value will be zero on success and
339f8f1e192Sniklasnon-zero otherwise.  Note that the conf_remove* functions consider not
340f8f1e192Sniklasfinding anything to remove as failure.
341f8f1e192Sniklas
3422040585eSniklasIdentification
3432040585eSniklas--------------
3442040585eSniklas
3452040585eSniklasISAKMP supports a lot of identity types, and we should too of course.
3462040585eSniklas
3471f98d23fSniklas* Phase 1, Main mode or Aggressive mode
3482040585eSniklas
3492040585eSniklasToday when we connect we do it based on the peer's IP address.  That does not
3502040585eSniklasautomatically mean we should do policy decision based on IPs, rather we should
3512040585eSniklaslook at the ID the peer provide and get policy info keyed on that.
3522040585eSniklas
3532040585eSniklasPerhaps we get an ID saying the peer is FQDN niklas.hallqvist.se, then our
3542040585eSniklaspolicy rules might look like:
3552040585eSniklas
3562040585eSniklas[IQ_FQDN]
3572040585eSniklas# If commented, internal verification is used
3582040585eSniklas#Verificator=	verify_fqdn
3592040585eSniklasAccept=		no
3602040585eSniklas
3612040585eSniklas[ID_FQDN niklas.hallqvist.se]
3622040585eSniklasPolicy=		MY_POLICY_001
3632040585eSniklas
3642040585eSniklas[MY_POLICY_001]
3652040585eSniklas# Whatever policy rules we might have.
3662040585eSniklasAccept=		yes
3672040585eSniklas
3682040585eSniklasWhich means niklas.hallqvist.se is allowed to negotiate SAs with us, but
3692040585eSniklasnoone else.
3702040585eSniklas
3711f98d23fSniklas* Phase 2, Quick mode
3722040585eSniklas
3732040585eSniklasIn quick mode the identities are implicitly the IP addresses of the peers,
3742040585eSniklaswhich must mean the IP addresses actually used for the ISAKMP tunnel.
3752b81057dSniklasOtherwise we today support IPV4_ADDR & IPV4_ADDR_SUBNET as ID types.
3762040585eSniklas
377fb1921ccSniklasX509-Certificates
378fb1921ccSniklas-----------------
379fb1921ccSniklasTo use RSA Signature mode you are required to generate certificates.
380fb1921ccSniklasThis can be done with ssleay, see man ssl. But the X509 certificates
381fb1921ccSniklasrequire a subjectAltname extension that can either be an IPV4 address,
382fb1921ccSniklasa User-FQDN or just FQDN.  ssleay can not create those extension,
383a42de217Sgrunkinstead use the x509test program in src/regress/sbin/isakmpd/x509 to
384d95e257cSclodermodify an existing certificate.  It will insert the subjectAltname
385d95e257cScloderextension and sign it with the provided private Key.  The resulting
386d95e257cSclodercertificate then needs to be stored in the directory pointed to by
387d95e257cScloder"Certs-directory" in section "X509-certificates".
388fb1921ccSniklas
3892040585eSniklasLicense to use
3902040585eSniklas--------------
3912040585eSniklas/*
392c6bfb327Sniklas * Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
3932040585eSniklas *
3942040585eSniklas * Redistribution and use in source and binary forms, with or without
3952040585eSniklas * modification, are permitted provided that the following conditions
3962040585eSniklas * are met:
3972040585eSniklas * 1. Redistributions of source code must retain the above copyright
3982040585eSniklas *    notice, this list of conditions and the following disclaimer.
3992040585eSniklas * 2. Redistributions in binary form must reproduce the above copyright
4002040585eSniklas *    notice, this list of conditions and the following disclaimer in the
4012040585eSniklas *    documentation and/or other materials provided with the distribution.
4022040585eSniklas *
4032040585eSniklas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
4042040585eSniklas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
4052040585eSniklas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
4062040585eSniklas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
4072040585eSniklas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
4082040585eSniklas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
4092040585eSniklas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
4102040585eSniklas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4112040585eSniklas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
4122040585eSniklas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4132040585eSniklas */
4142040585eSniklas
415