Commit cfb1587e authored by Evan Hunt's avatar Evan Hunt

2619. [func] Add support for RFC 5011, automatic trust anchor

			maintenance.  The new "managed-keys" statement can
			be used in place of "trusted-keys" for zones which
			support this protocol.  (Note: this syntax is
			expected to change prior to 9.7.0 final.) [RT #19248]
parent d3907d27
2619. [func] Add support for RFC 5011, automatic trust anchor
maintenance. The new "managed-keys" statement can
be used in place of "trusted-keys" for zones which
support this protocol. (Note: this syntax is
expected to change prior to 9.7.0 final.) [RT #19248]
2618. [bug] The sdb and sdlz db_interator_seek() methods could
loop infinitely. [RT #19847]
......
BIND 9.7.0 introduces support for RFC 5011, dynamic trust anchor
management. Using this feature allows named to keep track of changes to
critical DNSSEC keys without any need for the operator to make changes to
configuration files.
As of 9.7.0a1, the syntax for using RFC5011 is expected to change, so
proper documentation has yet to be written. This file is intended to
provide enough information to get started.
AUTHORITATIVE SERVER
--------------------
To set up an authoritative zone for RFC5011 trust anchor maintenance,
generate two (or more) key signing keys (KSKs) for the zone. Sign the zone
with one of them; this is the "active" KSK. All KSK's which do not sign
the zone are "stand-by" keys.
Any validating resolver which is configured to use the active KSK as an
RFC5011-managed trust anchor will take note of the stand-by KSKs in the
zone's DNSKEY RRset, and store them for future reference. The resolver
will recheck the zone periodically, and after 30 days, if the new key is
still there, then the key will be accepted by the resolver as a valid
trust anchor for the zone.
At any time after this 30-day acceptance timer has expired, the active
KSK can be revoked and the zone can be "rolled over" to one of the
standby KSKs.
To revoke a key, the new command "dnssec-revoke" has been added. This adds
the REVOKED bit to the key flags and re-generates the K*.key and K*.private
files.
After revoking the active key, the zone must be signed with both the
revoked KSK and the new active KSK. Once a key has been revoked and
used to sign the DNSKEY RRset in which it appears, that key will never
again be accepted as a valid trust anchor by the resolver. However,
validation can proceed using the new active key (which had been accepted
by the resolver when it was a stand-by key).
See RFC 5011 for more details on key rollover scenarios.
VALIDATING RESOLVER
-------------------
NOTE: This is expected to change before 9.7.0 is final!
To configure a validating resolver to use RFC5011 to maintain a trust
anchor, configure the trust anchor using a "managed-keys" statement
instead of a "trusted-keys" statement.
The syntax for "managed-keys" is identical to that for "trusted-keys".
However, whereas a trusted key is trusted permanently until it is removed
from named.conf, a managed key is only trusted for as long as it takes to
initialize RFC5011 key maintenance.
When named loads for the first time with a managed key configured, it
will fetch the DNSKEY RRset directly from the zone apex and check its
signature against the key specified in the "managed-keys" statement.
If it is validly signed, then the DNSKEY RRset is used as the basis for a
new managed keys database.
From that point on, when named loads, it will see the "managed-keys"
statement, check to make sure RFC5011 key maintenance has already been
initialized for the specified zone, and if so, it will simply move on.
No action will be taken unless a key is *removed* from the "managed-keys"
statement--in which case that zone is removed from the managed keys
database as well, and RFC5011 key maintenance will no longer be used.
......@@ -13,7 +13,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: Makefile.in,v 1.35 2008/11/07 02:28:49 marka Exp $
# $Id: Makefile.in,v 1.36 2009/06/30 02:52:32 each Exp $
srcdir = @srcdir@
VPATH = @srcdir@
......@@ -40,18 +40,19 @@ LIBS = ${DNSLIBS} ${ISCLIBS} @LIBS@
# Alphabetically
TARGETS = dnssec-keygen@EXEEXT@ dnssec-signzone@EXEEXT@ \
dnssec-keyfromlabel@EXEEXT@ dnssec-dsfromkey@EXEEXT@
dnssec-keyfromlabel@EXEEXT@ dnssec-dsfromkey@EXEEXT@ \
dnssec-revoke@EXEEXT@
OBJS = dnssectool.@O@
SRCS = dnssec-dsfromkey.c dnssec-keyfromlabel.c dnssec-keygen.c \
dnssec-signzone.c dnssectool.c
dnssec-revoke.c dnssec-signzone.c dnssectool.c
MANPAGES = dnssec-dsfromkey.8 dnssec-keyfromlabel.8 dnssec-keygen.8 \
dnssec-signzone.8
dnssec-revoke.8 dnssec-signzone.8
HTMLPAGES = dnssec-dsfromkey.html dnssec-keyfromlabel.html \
dnssec-keygen.html dnssec-signzone.html
dnssec-keygen.html dnssec-revoke.html dnssec-signzone.html
MANOBJS = ${MANPAGES} ${HTMLPAGES}
......@@ -77,6 +78,10 @@ dnssec-signzone@EXEEXT@: dnssec-signzone.@O@ ${OBJS} ${DEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
dnssec-signzone.@O@ ${OBJS} ${LIBS}
dnssec-revoke@EXEEXT@: dnssec-revoke.@O@ ${OBJS} ${DEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
dnssec-revoke.@O@ ${OBJS} ${LIBS}
doc man:: ${MANOBJS}
docclean manclean maintainer-clean::
......
......@@ -14,7 +14,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-keyfromlabel.c,v 1.6 2009/05/07 23:47:44 tbox Exp $ */
/* $Id: dnssec-keyfromlabel.c,v 1.7 2009/06/30 02:52:32 each Exp $ */
/*! \file */
......@@ -64,7 +64,7 @@ usage(void) {
fprintf(stderr, " -n nametype: ZONE | HOST | ENTITY | USER | OTHER\n");
fprintf(stderr, " (DNSKEY generation defaults to ZONE\n");
fprintf(stderr, " -c <class> (default: IN)\n");
fprintf(stderr, " -f keyflag: KSK\n");
fprintf(stderr, " -f keyflag (KSK or REVOKE)\n");
fprintf(stderr, " -t <type>: "
"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
"(default: AUTHCONF)\n");
......@@ -87,7 +87,7 @@ main(int argc, char **argv) {
dst_key_t *key = NULL, *oldkey;
dns_fixedname_t fname;
dns_name_t *name;
isc_uint16_t flags = 0, ksk = 0;
isc_uint16_t flags = 0, ksk = 0, revoke = 0;
dns_secalg_t alg;
isc_boolean_t null_key = ISC_FALSE;
isc_mem_t *mctx = NULL;
......@@ -125,6 +125,9 @@ main(int argc, char **argv) {
case 'f':
if (strcasecmp(isc_commandline_argument, "KSK") == 0)
ksk = DNS_KEYFLAG_KSK;
else if (strcasecmp(isc_commandline_argument,
"REVOKE") == 0)
revoke = DNS_KEYFLAG_REVOKE;
else
fatal("unknown flag '%s'",
isc_commandline_argument);
......@@ -238,8 +241,10 @@ main(int argc, char **argv) {
if ((options & DST_TYPE_KEY) != 0) /* KEY */
flags |= signatory;
else if ((flags & DNS_KEYOWNER_ZONE) != 0) /* DNSKEY */
else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
flags |= ksk;
flags |= revoke;
}
if (protocol == -1)
protocol = DNS_KEYPROTO_DNSSEC;
......
......@@ -29,7 +29,7 @@
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-keygen.c,v 1.85 2009/06/17 23:53:04 tbox Exp $ */
/* $Id: dnssec-keygen.c,v 1.86 2009/06/30 02:52:32 each Exp $ */
/*! \file */
......@@ -102,7 +102,7 @@ usage(void) {
fprintf(stderr, " -c <class> (default: IN)\n");
fprintf(stderr, " -d <digest bits> (0 => max, default)\n");
fprintf(stderr, " -e use large exponent (RSAMD5/RSASHA1 only)\n");
fprintf(stderr, " -f keyflag: KSK\n");
fprintf(stderr, " -f keyflag (KSK or REVOKE)\n");
fprintf(stderr, " -g <generator> use specified generator "
"(DH only)\n");
fprintf(stderr, " -t <type>: "
......@@ -130,7 +130,7 @@ main(int argc, char **argv) {
dst_key_t *key = NULL, *oldkey;
dns_fixedname_t fname;
dns_name_t *name;
isc_uint16_t flags = 0, ksk = 0;
isc_uint16_t flags = 0, ksk = 0, revoke = 0;
dns_secalg_t alg;
isc_boolean_t conflict = ISC_FALSE, null_key = ISC_FALSE;
isc_mem_t *mctx = NULL;
......@@ -182,6 +182,9 @@ main(int argc, char **argv) {
case 'f':
if (strcasecmp(isc_commandline_argument, "KSK") == 0)
ksk = DNS_KEYFLAG_KSK;
else if (strcasecmp(isc_commandline_argument,
"REVOKE") == 0)
revoke = DNS_KEYFLAG_REVOKE;
else
fatal("unknown flag '%s'",
isc_commandline_argument);
......@@ -423,8 +426,10 @@ main(int argc, char **argv) {
if ((options & DST_TYPE_KEY) != 0) /* KEY / HMAC */
flags |= signatory;
else if ((flags & DNS_KEYOWNER_ZONE) != 0) /* DNSKEY */
else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
flags |= ksk;
flags |= revoke;
}
if (protocol == -1)
protocol = DNS_KEYPROTO_DNSSEC;
......
.\" Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
.\" PERFORMANCE OF THIS SOFTWARE.
.\"
.\" $Id: dnssec-revoke.8,v 1.2 2009/06/30 02:52:32 each Exp $
.\"
.hy 0
.ad l
.\"Generated by db2man.xsl. Don't modify this, modify the source.
.de Sh \" Subsection
.br
.if t .Sp
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Ip \" List item
.br
.ie \\n(.$>=3 .ne \\$3
.el .ne 3
.IP "\\$1" \\$2
..
.TH "DNSSEC-REVOKE" 8 "June 1, 2009" "" ""
.SH NAME
dnssec-revoke \- Set the REVOKED bit on a DNSSEC key
.SH "SYNOPSIS"
.HP 14
\fBdnssec\-revoke\fR [\fB\-v\ \fIlevel\fR\fR] [\fB\-d\ \fIdirectory\fR\fR] [\fB\-F\fR] {keyfile}
.SH "DESCRIPTION"
.PP
\fBdnssec\-revoke\fR reads a DNSSEC key file, sets the REVOKED bit on the key as defined in RFC 5011, and creates a new pair of key files containing the now\- revoked key\&.
.SH "OPTIONS"
.TP
\-d \fIdirectory\fR
Sets the directory in which the key files are to reside\&.
.TP
\-v \fIlevel\fR
Sets the debugging level\&.
.TP
\-F
Force overwrite: Causes \fBdnssec\-revoke\fR to write the new key pair even if a file already exists matching the algorithm and key ID of the revoked key\&.
.SH "SEE ALSO"
.PP
\fBdnssec\-keygen\fR(8), BIND 9 Administrator Reference Manual, RFC 5011\&.
.SH "AUTHOR"
.PP
Internet Systems Consortium
/*
* Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-revoke.c,v 1.2 2009/06/30 02:52:32 each Exp $ */
/*! \file */
#include <config.h>
#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/errno.h>
#include <isc/buffer.h>
#include <isc/commandline.h>
#include <isc/entropy.h>
#include <isc/hash.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/string.h>
#include <isc/util.h>
#include <dns/keyvalues.h>
#include <dns/result.h>
#include <dst/dst.h>
#include "dnssectool.h"
const char *program = "dnssec-revoke";
int verbose;
static isc_mem_t *mctx = NULL;
static void
usage(void) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s [options] keyfile\n\n", program);
fprintf(stderr, "Version: %s\n", VERSION);
fprintf(stderr, " -f: force ovewrite\n");
fprintf(stderr, " -d directory: use directory for key files\n");
fprintf(stderr, " -h: help\n");
fprintf(stderr, " -r: remove old keyfiles after "
"creating revoked version\n");
fprintf(stderr, " -v level: set level of verbosity\n");
fprintf(stderr, "Output:\n");
fprintf(stderr, " K<name>+<alg>+<new id>.key, "
"K<name>+<alg>+<new id>.private\n");
exit (-1);
}
int
main(int argc, char **argv) {
isc_result_t result;
char *filename = NULL, *dir= NULL;
char newname[1024], oldname[1024];
char keystr[KEY_FORMATSIZE];
char *endp;
int ch;
isc_entropy_t *ectx = NULL;
dst_key_t *key = NULL;
isc_uint32_t flags;
isc_buffer_t buf;
isc_boolean_t force = ISC_FALSE;
isc_boolean_t remove = ISC_FALSE;
if (argc == 1)
usage();
result = isc_mem_create(0, 0, &mctx);
if (result != ISC_R_SUCCESS)
fatal("Out of memory");
dns_result_register();
isc_commandline_errprint = ISC_FALSE;
while ((ch = isc_commandline_parse(argc, argv, "d:fhrv:")) != -1) {
switch (ch) {
case 'd':
dir = isc_commandline_argument;
break;
case 'f':
force = ISC_TRUE;
break;
case 'r':
remove = ISC_TRUE;
break;
case 'v':
verbose = strtol(isc_commandline_argument, &endp, 0);
if (*endp != '\0')
fatal("-v must be followed by a number");
break;
case '?':
if (isc_commandline_option != '?')
fprintf(stderr, "%s: invalid argument -%c\n",
program, isc_commandline_option);
/* Falls into */
case 'h':
usage();
default:
fprintf(stderr, "%s: unhandled option -%c\n",
program, isc_commandline_option);
exit(1);
}
}
if (argc < isc_commandline_index + 1 ||
argv[isc_commandline_index] == NULL)
fatal("The key file name was not specified");
if (argc > isc_commandline_index + 1)
fatal("Extraneous arguments");
if (dir == NULL)
dir = dirname(argv[isc_commandline_index]);
filename = argv[isc_commandline_index];
if (ectx == NULL)
setup_entropy(mctx, NULL, &ectx);
result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
if (result != ISC_R_SUCCESS)
fatal("Could not initialize hash");
result = dst_lib_init(mctx, ectx,
ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
if (result != ISC_R_SUCCESS)
fatal("Could not initialize dst");
isc_entropy_stopcallbacksources(ectx);
result = dst_key_fromnamedfile(filename,
DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
mctx, &key);
if (result != ISC_R_SUCCESS)
fatal("Invalid keyfile name %s: %s",
filename, isc_result_totext(result));
if (verbose > 2) {
char keystr[KEY_FORMATSIZE];
key_format(key, keystr, sizeof(keystr));
fprintf(stderr, "%s: %s\n", program, keystr);
}
flags = dst_key_flags(key);
if ((flags & DNS_KEYFLAG_REVOKE) == 0) {
dst_key_setflags(key, flags | DNS_KEYFLAG_REVOKE);
isc_buffer_init(&buf, newname, sizeof(newname));
dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
if (access(newname, F_OK) == 0 && !force) {
fatal("Key file %s already exists; "
"use -f to force overwrite", newname);
}
result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
dir);
if (result != ISC_R_SUCCESS) {
key_format(key, keystr, sizeof(keystr));
fatal("Failed to write key %s: %s", keystr,
isc_result_totext(result));
}
printf("%s\n", newname);
isc_buffer_clear(&buf);
dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
printf("%s\n", newname);
/*
* Remove old key file, if told to (and if
* it isn't the same as the new file)
*/
if (remove && dst_key_alg(key) != DST_ALG_RSAMD5) {
isc_buffer_init(&buf, oldname, sizeof(oldname));
dst_key_setflags(key, flags & ~DNS_KEYFLAG_REVOKE);
dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
if (strcmp(oldname, newname) == 0)
goto cleanup;
if (access(oldname, F_OK) == 0)
unlink(oldname);
isc_buffer_clear(&buf);
dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
if (access(oldname, F_OK) == 0)
unlink(oldname);
}
} else {
key_format(key, keystr, sizeof(keystr));
fatal("Key %s is already revoked", keystr);
}
cleanup:
dst_key_free(&key);
dst_lib_destroy();
isc_hash_destroy();
cleanup_entropy(&ectx);
if (verbose > 10)
isc_mem_stats(mctx, stdout);
isc_mem_destroy(&mctx);
return (0);
}
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "&#8212;">]>
<!--
- Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- $Id: dnssec-revoke.docbook,v 1.2 2009/06/30 02:52:32 each Exp $ -->
<refentry id="man.dnssec-revoke">
<refentryinfo>
<date>June 1, 2009</date>
</refentryinfo>
<refmeta>
<refentrytitle><application>dnssec-revoke</application></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo>BIND9</refmiscinfo>
</refmeta>
<refnamediv>
<refname><application>dnssec-revoke</application></refname>
<refpurpose>Set the REVOKED bit on a DNSSEC key</refpurpose>
</refnamediv>
<docinfo>
<copyright>
<year>2009</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
<refsynopsisdiv>
<cmdsynopsis>
<command>dnssec-revoke</command>
<arg><option>-hr</option><arg>
<arg><option>-v <replaceable class="parameter">level</replaceable></option></arg>
<arg><option>-d <replaceable class="parameter">directory</replaceable></option></arg>
<arg><option>-f</option></arg>
<arg choice="req">keyfile</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>DESCRIPTION</title>
<para><command>dnssec-revoke</command>
reads a DNSSEC key file, sets the REVOKED bit on the key as defined
in RFC 5011, and creates a new pair of key files containing the now-
revoked key.
</para>
</refsect1>
<refsect1>
<title>OPTIONS</title>
<variablelist>
<varlistentry>
<term>-h</term>
<listitem>
<para>
Emit usage message and exit.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-d <replaceable class="parameter">directory</replaceable></term>
<listitem>
<para>
Sets the directory in which the key files are to reside.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-r</term>
<listitem>
<para>
After writing the new keyset files remove the original keyset
files.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-v <replaceable class="parameter">level</replaceable></term>
<listitem>
<para>
Sets the debugging level.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-f</term>
<listitem>
<para>
Force overwrite: Causes <command>dnssec-revoke</command> to
write the new key pair even if a file already exists matching
the algorithm and key ID of the revoked key.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>SEE ALSO</title>
<para><citerefentry>
<refentrytitle>dnssec-keygen</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>,
<citetitle>BIND 9 Administrator Reference Manual</citetitle>,
<citetitle>RFC 5011</citetitle>.
</para>
</refsect1>
<refsect1>
<title>AUTHOR</title>
<para><corpauthor>Internet Systems Consortium</corpauthor>
</para>
</refsect1>
</refentry><!--
- Local variables:
- mode: sgml
- End:
-->
<!--
- Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
-
- Permission to use, copy, modify, and distribute this software for any
- purpose with or without fee is hereby granted, provided that the above