Subversion Repositories libspf2

Compare Revisions

Ignore whitespace Rev 19 → Rev 29

/branches/lenny/debian/spfquery.1
File deleted
/branches/lenny/debian/control
1,11 → 1,12
Source: libspf2
Priority: optional
Section: libs
Maintainer: Magnus Holmgren <magnus@kibibyte.se>
Maintainer: Magnus Holmgren <holmgren@debian.org>
Build-Depends: debhelper (>> 5), cdbs, dpatch, autotools-dev
Standards-Version: 3.7.2
XS-Vcs-Svn: svn://svn.kibibyte.se/libspf2/trunk
XS-Vcs-Browser: http://svn.kibibyte.se/libspf2
Standards-Version: 3.7.3
Vcs-Svn: svn://svn.kibibyte.se/libspf2/trunk
Vcs-Browser: http://svn.kibibyte.se/libspf2
Homepage: http://www.libspf2.org/
 
Package: libspf2-dev
Section: libdevel
18,8 → 19,6
records and make sure that the email is authorized by the domain name
that it is coming from. This prevents email forgery, commonly used by
spammers, scammers and email viruses/worms.
.
Homepage: http://www.libspf2.org/
 
Package: libspf2-2
Section: libs
34,8 → 33,6
records and make sure that the email is authorized by the domain name
that it is coming from. This prevents email forgery, commonly used by
spammers, scammers and email viruses/worms.
.
Homepage: http://www.libspf2.org/
 
Package: spfquery
Section: mail
52,5 → 49,3
.
This package contains simple utilities that use libspf2 to test and
query SPF records.
.
Homepage: http://www.libspf2.org/
/branches/lenny/debian/changelog
1,10 → 1,23
libspf2 (1.2.5.dfsg-5+lenny1) testing-security; urgency=high
 
* [CVE-2008-2469] 50_dns_resolv_bufoverflow.dpatch: Fix buffer overflows
in DNS response parsing.
 
-- Magnus Holmgren <holmgren@debian.org> Sun, 19 Oct 2008 22:14:18 +0200
 
libspf2 (1.2.5.dfsg-5) unstable; urgency=low
 
* 43_new_explanation_url.dpatch: Bring default explanation up to date by
referring to www.openspf.org instead of spf.pobox.com.
* Add spfquery(1) manpage.
* Fix format of copyright notice in debian/copyright.
* Move upstream homepage URL to the new Homepage control field, rename
VCS control fields, and update Standards-Version.
* A few cosmetic adjustments including fixing the indentation of the
1.2.5.dfsg-3 entry below (Closes: #465466).
* New maintainer email address.
 
-- Magnus Holmgren <magnus@kibibyte.se> Thu, 13 Sep 2007 14:26:30 +0200
-- Magnus Holmgren <holmgren@debian.org> Fri, 18 Apr 2008 17:51:28 +0200
 
libspf2 (1.2.5.dfsg-4) unstable; urgency=low
 
26,8 → 39,8
a permanent error as well (Closes: #435139).
* 41_none_not_neutral.dpatch: Use a diffent explanation for
SPF_RESULT_NONE than the one for SPF_RESULT_NEUTRAL (Closes: #435140).
* 42_empty_sender.dpatch: Use the HELO identity in MAIL FROM checks if
the sender address has been set to the empty string (Closes: #431239).
* 42_empty_sender.dpatch: Use the HELO identity in MAIL FROM checks if
the sender address has been set to the empty string (Closes: #431239).
* debian/control: Add XS-Vcs-* fields.
 
-- Magnus Holmgren <magnus@kibibyte.se> Wed, 22 Aug 2007 17:13:27 +0200
/branches/lenny/debian/patches/50_dns_resolv_bufoverflow.dpatch
0,0 → 1,325
#! /bin/sh /usr/share/dpatch/dpatch-run
## 50_permanent_include_errors.dpatch by Shevek <shevek@anarres.org>, edited by Magnus Holmgren
##
## DP: Fix CVE-2008-2469 - buffer overflows handling DNS responses.
 
@DPATCH@
 
--- libspf2/src/libspf2/spf_dns_resolv.c.orig 2008-09-20 19:36:57.000000000 +0200
+++ libspf2/src/libspf2/spf_dns_resolv.c 2008-09-20 19:39:08.000000000 +0200
@@ -110,7 +110,8 @@
int nrec;
int cnt;
- u_char response[2048];
+ u_char *responsebuf;
+ size_t responselen;
int dns_len;
@@ -127,11 +128,13 @@
char name_buf[ NS_MAXDNAME ];
int prio;
- int rdlen;
- const u_char *rdata, *rdata_end;
+ size_t rdlen;
+ const u_char *rdata;
+#if HAVE_DECL_RES_NINIT
void *res_spec;
struct __res_state *res_state;
+#endif
SPF_ASSERT_NOTNULL(spf_dns_server);
@@ -140,10 +143,12 @@
SPF_ASSERT_NOTNULL(spfhook);
#endif
+#if HAVE_DECL_RES_NINIT
res_spec = pthread_getspecific(res_state_key);
if (res_spec == NULL) {
res_state = (struct __res_state *)
malloc(sizeof(struct __res_state));
+ memset(res_state, 0, sizeof(struct __res_state));
if (res_ninit(res_state) != 0) {
SPF_error("Failed to call res_ninit()");
}
@@ -152,20 +157,45 @@
else {
res_state = (struct __res_state *)res_spec;
}
+#endif
+
+ responselen = 2048;
+ responsebuf = (u_char *)malloc(responselen);
+ memset(responsebuf, 0, responselen);
+
+ /*
+ * Retry the lookup until our response buffer is big enough.
+ *
+ * This loop repeats until either we fail a lookup or we succeed.
+ * The size of the response buffer is monotonic increasing, so eventually we
+ * must either succeed, or we try to malloc more RAM than we can.
+ *
+ * The Linux man pages do not describe res_nquery adequately. Solaris says:
+ *
+ * The res_nquery() and res_query() routines return a length that may be bigger
+ * than anslen. In that case, retry the query with a larger buf. The answer to the
+ * second query may be larger still], so it is recommended that you supply a buf
+ * larger than the answer returned by the previous query. answer must be large
+ * enough to receive a maximum UDP response from the server or parts of the answer
+ * will be silently discarded. The default maximum UDP response size is 512 bytes.
+ */
+ for (;;) {
/*
* try resolving the name
*/
#if HAVE_DECL_RES_NINIT
dns_len = res_nquery(res_state, domain, ns_c_in, rr_type,
- response, sizeof(response));
+ responsebuf, responselen);
#else
dns_len = res_query(domain, ns_c_in, rr_type,
- response, sizeof(response));
+ responsebuf, responselen);
#endif
if ( dns_len < 0 ) {
+ /* We failed to perform a lookup. */
/* This block returns unconditionally. */
+ free(responsebuf);
if ( spf_dns_server->debug )
SPF_debugf( "query failed: err = %d %s (%d): %s",
dns_len, hstrerror( SPF_h_errno ), SPF_h_errno,
@@ -178,6 +208,25 @@
return SPF_dns_rr_new_init(spf_dns_server,
domain, rr_type, 0, SPF_h_errno);
}
+ else if (dns_len > responselen) {
+ /* We managed a lookup but our buffer was too small. */
+ responselen = dns_len + (dns_len >> 1);
+#if 0
+ /* Sanity-trap - we should never hit this. */
+ if (responselen > 1048576) { /* One megabyte. */
+ free(responsebuf);
+ return SPF_dns_rr_new_init(spf_dns_server,
+ domain, rr_type, 0, SPF_h_errno);
+ }
+#endif
+ responsebuf = realloc(responsebuf, responselen);
+ }
+ else {
+ /* We managed a lookup, and our buffer was large enough. */
+ responselen = dns_len;
+ break;
+ }
+ }
/*
* initialize stuff
@@ -185,12 +234,13 @@
spfrr = SPF_dns_rr_new_init(spf_dns_server,
domain, rr_type, 0, NETDB_SUCCESS);
- err = ns_initparse( response, dns_len, &ns_handle );
+ err = ns_initparse(responsebuf, responselen, &ns_handle);
if ( err < 0 ) { /* 0 or -1 */
if ( spf_dns_server->debug )
SPF_debugf( "ns_initparse failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
@@ -226,6 +276,7 @@
if ( spf_dns_server->debug > 1 )
SPF_debugf( "ns_parserr failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
@@ -257,8 +308,8 @@
break;
case ns_t_ns:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -271,8 +322,8 @@
break;
case ns_t_cname:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -286,8 +337,8 @@
case ns_t_mx:
prio = ns_get16( rdata );
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + sizeof( responselen ),
rdata + NS_INT16SZ,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -300,14 +351,13 @@
break;
case ns_t_txt:
- rdata_end = rdata + rdlen;
SPF_debugf( "TXT: (%d) \"%.*s\"",
rdlen, rdlen-1, rdata+1 );
break;
case ns_t_ptr:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -341,18 +391,21 @@
{
case ns_t_a:
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- sizeof( spfrr->rr[cnt]->a ) ) != SPF_E_SUCCESS )
+ sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
- memmove( &spfrr->rr[cnt]->a, rdata, sizeof( spfrr->rr[cnt]->a ) );
+ }
+ memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a));
cnt++;
break;
case ns_t_aaaa:
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- sizeof( spfrr->rr[cnt]->aaaa ) ) != SPF_E_SUCCESS )
+ sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
- memmove( &spfrr->rr[cnt]->aaaa, rdata, sizeof( spfrr->rr[cnt]->aaaa ) );
-
+ }
+ memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa));
cnt++;
break;
@@ -364,8 +417,8 @@
break;
case ns_t_mx:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress(responsebuf,
+ responsebuf + responselen,
rdata + NS_INT16SZ,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -373,12 +426,15 @@
if ( spf_dns_server->debug > 1 )
SPF_debugf( "ns_name_uncompress failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- strlen( name_buf ) + 1 ) != SPF_E_SUCCESS )
+ strlen(name_buf) + 1 ) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
strcpy( spfrr->rr[cnt]->mx, name_buf );
cnt++;
@@ -390,8 +446,12 @@
u_char *src, *dst;
size_t len;
- if ( SPF_dns_rr_buf_realloc( spfrr, cnt, rdlen ) != SPF_E_SUCCESS )
+ /* Just rdlen is enough because there is at least one
+ * length byte. */
+ if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
dst = (u_char *)(spfrr->rr[cnt]->txt);
len = 0;
@@ -400,15 +460,22 @@
{
len = *src;
src++;
+ rdlen--;
+
+ /* Avoid buffer overrun if len is junk. */
+ if (len > rdlen)
+ len = rdlen;
memcpy( dst, src, len );
dst += len;
src += len;
- rdlen -= len + 1;
+ rdlen -= len;
}
*dst = '\0';
} else {
- if ( SPF_dns_rr_buf_realloc( spfrr, cnt, 1 ) != SPF_E_SUCCESS )
+ if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
spfrr->rr[cnt]->txt[0] = '\0';
}
@@ -416,8 +483,8 @@
break;
case ns_t_ptr:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress(responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -425,12 +492,15 @@
if ( spf_dns_server->debug > 1 )
SPF_debugf( "ns_name_uncompress failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- strlen( name_buf ) + 1 ) != SPF_E_SUCCESS )
+ strlen(name_buf) + 1) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
strcpy( spfrr->rr[cnt]->ptr, name_buf );
cnt++;
@@ -447,6 +517,7 @@
if ( spfrr->num_rr == 0 )
spfrr->herrno = NO_DATA;
+ free(responsebuf);
return spfrr;
}
/branches/lenny/debian/patches/42_empty_sender.dpatch
1,10 → 1,9
#! /bin/sh /usr/share/dpatch/dpatch-run
## 42_empty_sender.dpatch by <magnus@proffe.kibibyte.se>
#!/bin/sh /usr/share/dpatch/dpatch-run
## 42_empty_sender.dpatch by Magnus Holmgren <magnus@kibibyte.se>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: If SPF_request_set_env_from() is called with from set to the empty
## DP: string, use the HELO identity. Also fix incorrect handling when
## DP: the local part is empty (but the "@" is present).
## DP: string (i.e. a DSN), use the HELO identity. Also fix incorrect
## DP: handling when the local part is empty (but the "@" is present).
 
@DPATCH@
diff -urNad trunk~/src/libspf2/spf_request.c trunk/src/libspf2/spf_request.c
/branches/lenny/debian/patches/00list
14,3 → 14,4
41_none_not_neutral
42_empty_sender
43_new_explanation_url
50_dns_resolv_bufoverflow
/branches/lenny/debian/spfquery.postinst
8,7 → 8,8
configure )
prev_version=$2
update-alternatives --install /usr/bin/spfquery spfquery /usr/bin/spfquery.$source_package 25
update-alternatives --install /usr/bin/spfquery spfquery /usr/bin/spfquery.$source_package 25 \
--slave /usr/share/man/man1/spfquery.1.gz spfquery.1.gz /usr/share/man/man1/spfquery.$source_package.1.gz
update-alternatives --install /usr/sbin/spfd spfd /usr/sbin/spfd.$source_package 25
;;
esac
/branches/lenny/debian/spfquery.manpages
0,0 → 1,0
debian/spfquery.libspf2.1
/branches/lenny/debian/copyright
9,8 → 9,8
 
Copyright:
 
This software is copyright (c) 2004-2005 by Wayne Schlitt <wayne@schlitt.net>
and Shevek <shevek@anarres.org>
Software copyright © 2004-2005 Wayne Schlitt <wayne@schlitt.net> and
Shevek <shevek@anarres.org>
 
You are free to distribute this software under the terms of the GNU Lesser
General Public License version 2.1 or the BSD license, at your choice.
/branches/lenny/debian/spfquery.libspf2.1
0,0 → 1,199
.\" Title: SPFQUERY
.\" Author: Magnus Holmgren <magnus@kibibyte.se>
.\" Date: 2007-09-06
.\" Manual: libspf2 manuals for Debian
.\" Source: libspf2 1.2.5
.\"
.TH "SPFQUERY" "1" "2007-09-06" "libspf2 1.2.5" "libspf2 manuals for Debian"
.\" disable hyphenation
.nh
.SH NAME
spfquery, spfquery.libspf2 \- checks if an IP address is an SPF-authorized SMTP sender for a domain.
.SH SYNOPSIS
.ad l
.HP 9
.B spfquery
.RB { \-i | \-\-ip }
.I ip\-address
.RB { -s | \-\-sender }
.RI [ local-part \fB@\fP] domain
.RB [{ \-h | \-\-helo }
.IR domain-name ]
.RB [ \-\-rcpt\-to
.IR email-address(es) ]
.RI [ CONTROL-OPTIONS ]
.HP 9
.B spfquery
.RB { \-f | \-\-file }
.IR datafile " [" CONTROL-OPTIONS ]
.HP 9
.B spfquery
.RB { \-\-help | \-v | \-\-version }
.ad b
.SH DESCRIPTION
This manual page documents briefly the
\fBspfquery\fR
command. It was written for the
Debian\*[R] distribution because the original program does not have a manual page.
.PP
\fBspfquery\fR performs Sender Policy Framework (SPF) authorization
checks based on the command-line arguments or data given in a file or
on standard input. For information on SPF see http://www.openspf.org.
.
.SH OPTIONS
Options are divided into two groups: Data options, which must be
given, though just enough of them to specify a query; and control
options, which are optional and control the local policy, behaviour
and output format of spfquery.
.PP
This programs follows the GNU \fBgetopt_long_only\fR(3) command line
syntax: Long options can be given with one or two dashes and can be
abbreviated to a prefix long enough to be non-ambiguous. If an option
starting with a single dash doesn't match a long option, it is taken
as a short option with a following parameter, if applicable. An equals
sign between the option name and the parameter is optional for both
short and long options.
.SS Data options
The
\fB\-\-file\fR option conflicts with all the other data options. The
\fB\-\-helo\fR and \fB\-\-rcpt\-to\fR are optional.
.TP
\fB\-f\fR, \fB\-\-file\fR \fIfilename\fR
Read SPF data from \fIfilename\fR. Specify \(lq-\(rq to read from standard input.
.sp
The file should consist of one line per query, each query line consisting of the IP address, sender adress, and optional HELO string, separated by spaces.
.sp
\fBNote\fP
Local parts containing spaces are currently not supported.
.TP
\fB\-i\fP, \fB\-\-ip\fP \fIip-address\fP
Specify the IP address of the remote host that is delivering the mail.
.TP
\fB\-s\fP, \fB\-\-sender\fP [\fIlocal-part\fP\fB@\fP]\fIdomain\fP
Specify the email address that was used as the envelope sender. If no
username (local part) is given, \(lqpostmaster\(rq will be assumed.
.TP
\fB\-h\fP, \fB\-\-helo\fP \fIdomain-name\fP
Specify that \fIdomain-name\fP was provided in the SMTP HELO (or EHLO) command.
.TP
\fB\-r\fP, \fB\-\-rcpt-to\fP \fIrcpt-address\fP[,\fIrcpt-address\fP,...]
Specify the recipients as comma-separated list. Any secondary mail exchangers of all
recipient domains are automatically authorized.
.
.SS Control options
.TP
\fB\-d\fP, \fB\-\-debug\fP[\fB=\fP\fIlevel\fP]
Turn on debugging output.
.TP
\fB\-l\fP, \fB\-\-local\fP \fIspf\-terms\fP
Test against \fIspf\-terms\fR before the final (implicit or explicit)
\(lqall\(rq in an SPF record. This can be used to implement a local policy for whitelisting.
.TP
\fB\-t, \fB\-\-trusted\fR [\fB1\fR]
Check the sender domain with trusted\-forwarder.org.
\fBThis is a non\-standard feature.\fR
.TP
\fB\-t\fP \fB0\fP, \fB\-\-trusted\fR \fB0\fP
Do not check the sender domain with trusted\-forwarder.org. This is the default.
.TP
\fB\-g\fP, \fB\-\-guess\fP \fIspf-mechanisms\fP
Test the sender domain against \fIspf\-mechanisms\fP if the domain has no SPF record.
.TP
\fB\-e\fP, \fB\-\-default\-explanation\fP \fIstring\fP
Default explanation string to use if the SPF record does not specify an expla\%nation string itself.
.TP
\fB\-m\fP, \fB\-\-max\-lookup\fP \fInumber\fP
Maximum number of DNS lookups to allow.
.TP
\fB\-c\fP, \fB\-\-sanitize\fP [\fB0\fP|\fB1\fP]
Do [not] sanitize the output by condensing conse\%cutive white\%space
into a single space and replacing non-printable characters with
question marks. Enabled by default.
.TP
\fB\-n\fP, \fB\-\-name\fP \fIhostname\fP
Use
\fIhostname\fP
as the name of the local system instead of
\(lqspfquery\(rq
(the name is used in the output).
.TP
\fB\-k\fP, \fB\-\-keep\-comments\fP
Print comments found when reading from a file.
.TP
\fB\-a\fP, \fB\-\-override\fP \fI...\fP
.TP
\fB\-z\fP, \fB\-\-fallback\fP \fI...\fP
Provide override and fallback SPF records for certain domains.
\fBNot implemented yet.\fP
\fBspfquery\fP
would act as if the speci\%fied records were present before and after any existing record, respectively, of those domains.
.TP
\fB\-\-help\fP
Show summary of options.
.TP
\fB\-v\fP, \fB\-\-version\fP
Show version of program.
.SH DIAGNOSTICS
The output ordinarily consists of four lines:
.IP 1. 4
the \fIresult code\fP;
.IP 2. 4
the \fIexplanation\fP, suitable for use in an SMTP response message, empty
except when a rejection (permanent or temporary) makes sense;
.IP 3. 4
the header comment on its own;
.IP 4. 4
the Received\-SPF header field as defined in RFC 4408 section 7,
incorporating the header comment.
.PP
If errors (including no SPF record found!) occur during processing,
one or more error blocks will be prepended.
These start with \(lqStartError\(lq and end with \(lqEndError\(lq.
.PP
The result codes and their corresponding exit codes are as follows:
.TP
.B 1 \(en neutral
The sender domain explicitly makes no assertion about the \fIip-address\fP.
This result must be interpreted exactly as if no SPF record at all existed.
.TP
.B 2 \(en pass
The \fIip-address\fP is authorized to send mail for the sender domain.
.TP
.B 3 \(en fail
The \fIip-address\fP is \fBunauthorized\fP to send mail for the sender domain.
.TP
.B 4 \(en softfail
The \fIip-address\fP is not authorized to send mail for the sender domain, but
the sender domain cannot or does not wish to make a strong assertion that no such mail can
ever come from it.
.TP
.B 5 \(en none
No SPF record was found.
.TP
.BR "6 \(en error" " (temporary)"
A transient error occurred (e.g. failure to reach a DNS server), preventing a
result from being reached.
.TP
.BR "7 \(en unknown" " (permanent error)"
One or more SPF records could not be interpreted.
.SH EXAMPLES
.nf
spfquery \-ip=11.22.33.44 \-sender=user@aol.com \-helo=spammer.tld
spfquery \-f test_data
echo "127.0.0.1 myname@mydomain.com helohost.com" | spfquery \-f \-
.fi
.SH SEE ALSO
\fBspftest\fR(1), \fBspfd\fR(8)
.SH AUTHOR
\fBspfquery\fP was written by Wayne Schlitt.
.PP
This manual page was written by Magnus Holmgren for the Debian\*[R]
system (but may be used by others). Heavily inspired by the spfquery manpage of
libmail\-spf\-query\-perl (\fBspfquery.mail\-spf\-query\-perl\fR(1)) by Julian Mehnle.
Also based on the command\-line help of spfquery.
.SH COPYRIGHT
Copyright \(co 2007 Magnus Holmgren. Permission is granted to copy,
distribute and/or modify this document under the terms of the BSD
License.
.PP
On Debian systems, the complete text of the BSD License can be found in /usr/share/common\-licenses/BSD.