diff options
47 files changed, 1130 insertions, 357 deletions
diff --git a/Makefile.in b/Makefile.in index 1d07c7f..bd1833d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -44,7 +44,7 @@ transform = @program_transform_name@ LIBTOOL = @LIBTOOL@ -XMLTO = xmlto --skip-validation +XMLTO = xmlto # The headers to distribute - making up the public interface of neon DIST_HEADERS = ne_request.h ne_session.h ne_utils.h ne_uri.h ne_socket.h \ @@ -1,3 +1,16 @@ +Changes in release 0.24.1: +* Add support for "GSS-Negotiate" Kerberos authentication scheme (from + Risko Gergely and Burjan Gabor). +* Disable Nagle to improve performance of small requests (thanks to + Jim Whitehead and Teng Xu). +* Fix compatibility with OpenSSL 0.9.6 (broken in 0.24.0). +* Fix prototype mismatch in ne_207.c. +* Define ssize_t from ne_request.h for Win32. +* Prevent segfault on zlib initialization failures. +* ne_sock_init does not fail if PRNG could not be seeded. +* Fix segfault in cookies code (Markus Mueller). +* Documentation updates. + Changes in release 0.24.0: * Major changes to XML interface: - have the start-element callback either accept, decline, abort, @@ -12,12 +12,14 @@ Greg Stein <gstein@lyra.org> Gregor Bornemann <Gregor.Bornemann@germany.sun.com> Jeff Johnson <jbj@redhat.com> Jeremy Elson <jelson@circlemud.org> +Jim Whitehead <ejw@cse.ucsc.edu> Johan Lindh <johan@linkdata.se> Justin Erenkrantz <jerenkrantz@apache.org> Kai Sommerfeld <kai.sommerfeld@germany.sun.com> Keith Wannamaker <keith@wannamaker.org> Lee Mallabone <lee0@callnetuk.com> Magnus Sirwiö <sirwio@hotmail.com> +Markus Mueller <markus-m.mueller@ubs.com> Michael Sobolev <mss@despair.spb.ru> Mike Rosellini <m@icopyright.com> Mo DeJong <mdejong@cygnus.com> @@ -27,13 +29,16 @@ Pawel Golaszewski <blues@ds.pg.gda.pl> Peter Boos <PediB@colorfullife.com> Peter Moulder <pjm@bofh.asn.au> rado <dzusto@yahoo.com> +Risko Gergely <risko@risko.hu> Rodney Dawes <dobey@ximian.com> Sam TH <sam@uchicago.edu> Sander Alberink <sander.alberink@cmg.nl> Sander Striker <striker@apache.org> Shane Mayer <shanemayer42@yahoo.com> Taisuke Yamada <tai@iij.ad.jp> +Teng Xu <txu@soe.ucsc.edu> Tom Bednarz <tombednarz@hotmail.com> +Tom Lee <i_am_gnomey@hotmail.com> Torsten Kalix <torsten.kalix@bredex.de> Wilfredo Sánchez <wsanchez@mit.edu> diff --git a/doc/biblio.xml b/doc/biblio.xml new file mode 100644 index 0000000..cda37a7 --- /dev/null +++ b/doc/biblio.xml @@ -0,0 +1,74 @@ +<bibliography id="biblio"> <!-- -*- xml -*- --> + +<biblioentry id="bib.ssltls"> + <abbrev>SSL-and-TLS</abbrev> + <title><ulink url="http://www.rtfm.com/sslbook/">SSL and + TLS: Designing and Building Secure Systems</ulink></title> + <author><firstname>Eric</firstname><surname>Rescorla</surname></author> + <isbn>0-201-62598-3</isbn> + <publisher><publishername>Addison-Wesley</publishername></publisher> + <pubdate>March 2001</pubdate> +</biblioentry> + +<biblioentry id="bib.xsltrec"> + <abbrev>REC-XML-names</abbrev> + <editor><firstname>James</firstname><surname>Clark</surname></editor> + <title><ulink url="http://www.w3.org/TR/xslt">XSL Transformations + (XSLT) Version 1.0</ulink></title> <publishername>W3C + Recommendation</publishername> <pubdate>16 November 1999</pubdate> +</biblioentry> + +<biblioentry id="bib.rfc2616"> + <abbrev>RFC2616</abbrev> + <title><ulink url="http://www.ietf.org/rfc/rfc2616.txt">Hypertext Transfer + Protocol—HTTP/1.1</ulink></title> + <authorgroup> + <author><firstname>Roy</firstname><surname>Fielding</surname></author> + <author><firstname>Jim</firstname><surname>Gettys</surname></author> + <author><firstname>Jeff</firstname><surname>Mogul</surname></author> + <author><firstname>Henrik</firstname><surname>Frystyk</surname></author> + <author><firstname>Larry</firstname><surname>Masinter</surname></author> + <author><firstname>Paul</firstname><surname>Leach</surname></author> + <author><firstname>Tim</firstname><surname>Berners-Lee</surname></author> + </authorgroup> + <publishername>IETF</publishername> + <pubdate>June 1999</pubdate> +</biblioentry> + +<biblioentry id="bib.rfc2518"> + <abbrev>RFC2518</abbrev> + <title><ulink url="http://www.ietf.org/rfc/rfc2518.txt">HTTP Extensions for Distributed Authoring—WEBDAV</ulink></title> + <authorgroup> + <author><firstname>Yaron</firstname><surname>Goland</surname></author> + <author><firstname>Jim</firstname><surname>Whitehead</surname></author> + <author><firstname>Asad</firstname><surname>Faizi</surname></author> + <author><firstname>Steve</firstname><surname>Carter</surname></author> + <author><firstname>Del</firstname><surname>Jensen</surname></author> + </authorgroup> + <publishername>IETF</publishername> + <pubdate>February 1999</pubdate> +</biblioentry> + +<biblioentry id="bib.rfc3280"> + <abbrev>RFC3280</abbrev> + <title><ulink url="http://www.ietf.org/rfc/rfc3280.txt">Internet X.509 Public Key Infrastructure + Certificate and Certificate Revocation List (CRL) Profile</ulink></title> + <authorgroup> + <author><firstname>Russel</firstname><surname>Housley</surname></author> + <author><firstname>Warwick</firstname><surname>Ford</surname></author> + <author><firstname>Tim</firstname><surname>Polk</surname></author> + <author><firstname>David</firstname><surname>Solo</surname></author> + </authorgroup> + <publishername>IETF</publishername> + <pubdate>April 2002</pubdate> +</biblioentry> + +<!-- RFCs: 2617 --> + +<!-- Other interesting RFCs: + + 3490 : Internationalizing Domain Names in Applications (IDNA) + 3493 : Basic Socket Interface Extensions for IPv6 + --> + +</bibliography> diff --git a/doc/manual.css b/doc/manual.css index 621feb5..feca0a1 100644 --- a/doc/manual.css +++ b/doc/manual.css @@ -7,8 +7,8 @@ div.legalnotice { font-size: 80%; margin-left: 2em; } a:visited { color: darkgreen; } -div.navheader { border-top: thin solid #bbf2bb; } -div.navfooter { border-bottom: thin solid #bbf2bb; } +div.navheader { border-top: 1px solid #bbf2bb; } +div.navfooter { border-bottom: 1px solid #bbf2bb; } div.funcprototype { margin-top: 0.2em; margin-left: 0.4em; margin-bottom: 0.2em; } @@ -25,7 +25,7 @@ div.funcsynopsis, div.cmdsynopsis { } div.warning { - border: thin solid #777777; + border: 1px solid #777777; } h1.title { border-bottom: thick solid #bbf2bb; padding-bottom: 0.1em; } @@ -39,4 +39,4 @@ h2, h3 { padding-left: 0.2em; padding-top: -0.1em; } h2 { background-color: #bbf2bb; font-size: 110%; padding-bottom: 0.3em; padding-top: 0.2em; spacing-top: 0.1em; } -h3 { border-bottom: thin solid #bbf2bb; } +h3 { border-bottom: 1px solid #bbf2bb; } diff --git a/doc/manual.xml b/doc/manual.xml index 893f007..06e7bca 100644 --- a/doc/manual.xml +++ b/doc/manual.xml @@ -31,9 +31,12 @@ <!ENTITY cdata "<emphasis>character-data</emphasis>"> <!ENTITY endelm "<emphasis>end-element</emphasis>"> -<!ENTITY section.xml SYSTEM "xml.xml"> <!ENTITY section.features SYSTEM "feat.xml"> <!ENTITY section.using SYSTEM "using.xml"> +<!ENTITY section.xml SYSTEM "xml.xml"> +<!ENTITY section.ssl SYSTEM "ssl.xml"> + +<!ENTITY biblio SYSTEM "biblio.xml"> <!ENTITY refneon SYSTEM "ref/neon.xml"> <!ENTITY refconfig SYSTEM "ref/config.xml"> @@ -43,8 +46,10 @@ <!ENTITY refopts SYSTEM "ref/opts.xml"> <!ENTITY refsslvfy SYSTEM "ref/sslvfy.xml"> <!ENTITY refsslcert SYSTEM "ref/sslcert.xml"> +<!ENTITY refsslcert2 SYSTEM "ref/sslcert2.xml"> +<!ENTITY refsslcertio SYSTEM "ref/sslcertio.xml"> <!ENTITY refssldname SYSTEM "ref/ssldname.xml"> -<!ENTITY refsslca SYSTEM "ref/sslca.xml"> +<!ENTITY refssltrust SYSTEM "ref/ssltrust.xml"> <!ENTITY refreq SYSTEM "ref/req.xml"> <!ENTITY refreqhdr SYSTEM "ref/reqhdr.xml"> <!ENTITY refstatus SYSTEM "ref/status.xml"> @@ -110,10 +115,17 @@ ignoring the WebDAV support if desired.</para> </chapter> <chapter id="api"> - <title>The neon API for the C language</title> + <title>The &neon; C language interface</title> + + <para>The documentation for the &neon; interface is split between + this chapter, which gives a broad introduction to the abstractions + exposed by the library, and <xref linkend="ref"/>, which gives a + function-by-function breakdown of the interface.</para> §ion.xml; +<!-- §ion.ssl; --> + </chapter> <reference id="ref"> @@ -148,9 +160,11 @@ ignoring the WebDAV support if desired.</para> &refauth; <!-- ne_set_server_auth --> &refshave; <!-- ne_shave --> &refinit; <!-- ne_sock_init --> - &refsslcert; <!-- ne_ssl_certificate --> + &refsslcert; <!-- ne_ssl_cert_identity --> + &refsslcert2; <!-- ne_ssl_cert_cmp --> + &refsslcertio; <!-- ne_ssl_cert_read --> &refssldname; <!-- ne_ssl_dname --> - &refsslca; <!-- ne_ssl_load_ca --> + &refssltrust; <!-- ne_ssl_load_ca --> &refsslvfy; <!-- ne_ssl_set_verify --> &refclicert; <!-- ne_ssl_client_cert --> &refstatus; <!-- ne_status --> @@ -162,6 +176,8 @@ ignoring the WebDAV support if desired.</para> </reference> +&biblio; + &fdl; </book> diff --git a/doc/ref/clicert.xml b/doc/ref/clicert.xml index 47a2ce0..c61404c 100644 --- a/doc/ref/clicert.xml +++ b/doc/ref/clicert.xml @@ -146,7 +146,7 @@ ne_ssl_set_clicert(sess, ccert); <refsect1> <title>See also</title> - <para><xref linkend="ne_ssl_certificate"/></para> + <para><xref linkend="ne_ssl_cert_read"/></para> </refsect1> </refentry> diff --git a/doc/ref/neon.xml b/doc/ref/neon.xml index 9857c63..7c11325 100644 --- a/doc/ref/neon.xml +++ b/doc/ref/neon.xml @@ -34,11 +34,38 @@ <refsect2> <title>Thread-safeness and global initialization</title> - <para>&neon; itself is implemented to be thread-safe (avoiding - any use of global state), but in some configurations makes use of - other libraries which require global initialization. The - <xref linkend="ne_sock_init"/> function should be called before - any other use of the &neon; library interface.</para> + <para>&neon; itself is implemented to be thread-safe (avoiding any + use of global state), but relies on the operating system providing + a thread-safe resolver interface. Modern operating systems offer + the thread-safe <function>getaddrinfo</function> interface, which + &neon; supports; some others implement + <function>gethostbyname</function> using thread-local + storage.</para> + + <para>To allow thread-safe use of the OpenSSL library, the + application must register some locking callbacks in accordance + with the <ulink + url="http://www.openssl.org/docs/crypto/threads.html">OpenSSL + documentation</ulink>.</para> + + <para>Some platforms and libraries used by &neon; require global + initialization before use; notably: + + <itemizedlist> + <listitem><simpara>OpenSSL requires global initialization to + load shared lookup tables.</simpara></listitem> + + <listitem><simpara>The SOCKS library requires initialization + before use.</simpara></listitem> + + <listitem><simpara>The Win32 socket library requires + initialization before use.</simpara></listitem> + </itemizedlist> + + The <xref linkend="ne_sock_init"/> function should be called + before any other use of &neon; to perform any necessary + initialization needed for the particular platform.</para> + </refsect2> <refsect2> @@ -91,11 +118,12 @@ <refsect2> <title>Argument validation</title> - <para>&neon; does not attempt to validate that arguments passed to - functions conform to the API (for instance, checking that pointer - arguments are not &null;). Any use of the &neon; API which is not - documented to produce a certain behaviour results in - <emphasis>undefined behaviour</emphasis>, by definition.</para> + <para>&neon; does not attempt to validate that the parameters + passed to functions conform to the API (for instance, checking + that pointer arguments are not &null;). Any use of the &neon; API + which is not documented to produce a certain behaviour results is + said to produce <emphasis>undefined behaviour</emphasis>; it is + likely that &neon; will segfault under these conditions.</para> </refsect2> @@ -111,6 +139,15 @@ </refsect2> <refsect2> + <title>User interaction</title> + + <para>As a pure library interface, &neon; will never produce + output on <constant>stdout</constant> or + <constant>stderr</constant>; all user interaction is the + responsibilty of the application.</para> + </refsect2> + + <refsect2> <title>Memory handling</title> <para>neon does not attempt to cope gracefully with an diff --git a/doc/ref/req.xml b/doc/ref/req.xml index 7c36d5c..0dfcd9a 100644 --- a/doc/ref/req.xml +++ b/doc/ref/req.xml @@ -53,7 +53,7 @@ parameter, combined with the <parameter>path</parameter> parameter.</para> <para>The <parameter>path</parameter> string used must conform to the <literal>abs_path</literal> definition given in RFC2396, with an optional "?query" part, and must be URI-escaped by the caller (for -instance, using <function>ne_path_escape</function>. If the string +instance, using <function>ne_path_escape</function>). If the string comes from an untrusted source, failure to perform URI-escaping results in a security vulnerability.</para> @@ -145,7 +145,7 @@ non-zero error code otherwise.</para> <title>Example</title> <para>An example of applying a <literal>MKCOL</literal> - operation to the resource at the location + operation to the resource at the location <literal>http://www.example.com/foo/bar/</literal>:</para> <programlisting>ne_session *sess = ne_session_create("http", "www.example.com", 80); diff --git a/doc/ref/sess.xml b/doc/ref/sess.xml index 8fcd9cb..93f0038 100644 --- a/doc/ref/sess.xml +++ b/doc/ref/sess.xml @@ -65,9 +65,9 @@ global initialization needed by any libraries used by &neon;.</para> <para>To enable SSL/TLS for the session, pass the string <literal>"https"</literal> as the <parameter>scheme</parameter> parameter, and either register a certificate verification function -(see <xref linkend="ne_ssl_set_verify"/>) or load the appropriate CA -certificate (see <xref linkend="ne_ssl_load_ca"/>, <xref -linkend="ne_ssl_load_default_ca"/>).</para> +(see <xref linkend="ne_ssl_set_verify"/>) or trust the appropriate +certificate (see <xref linkend="ne_ssl_trust_cert"/>, <xref +linkend="ne_ssl_trust_default_ca"/>).</para> <para>If an HTTP proxy server should be used for the session, <function>ne_session_proxy</function> must be called giving the @@ -91,7 +91,7 @@ session pointer produces undefined behaviour.</para> <para>The hostname passed to <function>ne_session_create</function> is resolved when the first -request using the session is dispached; a DNS resolution failure can +request using the session is dispatched; a DNS resolution failure can only be detected at that time (using the <literal>NE_LOOKUP</literal> error code); see <xref linkend="ne_request_dispatch"/> for details.</para> @@ -117,7 +117,7 @@ ne_session_destroy(sess); <refsect1> <title>See Also</title> - <para><xref linkend="ne_ssl_set_verify"/>, <xref linkend="ne_ssl_load_ca"/>, <xref linkend="ne_sock_init"/></para> + <para><xref linkend="ne_ssl_set_verify"/>, <xref linkend="ne_ssl_trust_cert"/>, <xref linkend="ne_sock_init"/></para> </refsect1> </refentry> diff --git a/doc/ref/shave.xml b/doc/ref/shave.xml index a5b745a..914699d 100644 --- a/doc/ref/shave.xml +++ b/doc/ref/shave.xml @@ -32,7 +32,8 @@ <para><function>ne_shave</function> returns a portion of <parameter>str</parameter> with any leading or trailing characters in the <parameter>whitespace</parameter> array removed. -<parameter>str</parameter> may be modified.</para> +<parameter>str</parameter> may be modified. Note that the return +value may not be equal to <parameter>str</parameter>.</para> </refsect1> diff --git a/doc/ref/sslcert.xml b/doc/ref/sslcert.xml index 46dbffd..d2d0376 100644 --- a/doc/ref/sslcert.xml +++ b/doc/ref/sslcert.xml @@ -1,66 +1,111 @@ - <refentry id="refsslcert"> - - <refmeta> - <refentrytitle>ne_ssl_certificate</refentrytitle> - <manvolnum>3</manvolnum> - </refmeta> - - <refnamediv> - <refname id="ne_ssl_certificate">ne_ssl_certificate</refname> - <refname id="ne_ssl_dname">ne_ssl_dname</refname> - <refpurpose>structures representing SSL certificates</refpurpose> - </refnamediv> - - <refsynopsisdiv> - - <funcsynopsis><funcsynopsisinfo>#include <ne_session.h> - -/* A simplified X.509 distinguished name. */ -typedef struct { - const char *country, *state, *locality, *organization; - const char *organizationalUnit; - const char *commonName; -} <type>ne_ssl_dname</type>; - -/* A simplified SSL certificate. */ -typedef struct { - const <type>ne_ssl_dname</type> *subject, *issuer; - const char *from, *until; -} <type>ne_ssl_certificate</type>; - -</funcsynopsisinfo></funcsynopsis> - - </refsynopsisdiv> - - <refsect1> - <title>Description</title> - - <para>The <type>ne_ssl_dname</type> structure is used to -represent a simplified X.509 distinguished name, as used in SSL -certificates; a distinguished name is used to uniquely identify an -entity. Along with the fields giving the geographical and -organizational location of the entity, the -<structfield>commonName</structfield> field will be assigned the DNS -hostname of the entity. The -<function>ne_ssl_readable_dname</function> function can be used to -create a single-line string out of an <type>ne_ssl_dname</type> -structure.</para> - - <para>The <type>ne_ssl_certificate</type> structure is used to -represent a simplified SSL certificate; containing the distinguished -names of the <firstterm>issuer</firstterm> and -<firstterm>subject</firstterm> of the certificate. The issuer is the -entity which has digitally signed the certificate to guarantee its -authenticity; the subject is the owner of the certificate. A -certificate is only valid for a certain period of time: the -<structfield>from</structfield> and <structfield>until</structfield> -contain strings giving the validity period.</para> - - </refsect1> - - <refsect1> - <title>See Also</title> - <para><xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_set_verify"/></para> - </refsect1> - - </refentry> +<refentry id="refcert"> + + <refmeta> + <refentrytitle>ne_ssl_cert_identity</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname id="ne_ssl_cert_identity">ne_ssl_cert_identity</refname> + <refname id="ne_ssl_cert_signedby">ne_ssl_cert_signedby</refname> + <refname id="ne_ssl_cert_issuer">ne_ssl_cert_issuer</refname> + <refname id="ne_ssl_cert_subject">ne_ssl_cert_subject</refname> + <refpurpose>functions to access certificate properties</refpurpose> + </refnamediv> + + <refsynopsisdiv> + + <funcsynopsis> + + <funcsynopsisinfo>#include <ne_ssl.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>const char *<function>ne_ssl_cert_identity</function></funcdef> + <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>const ne_ssl_certificate *<function>ne_ssl_cert_signedby</function></funcdef> + <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>const ne_ssl_dname *<function>ne_ssl_cert_subject</function></funcdef> + <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>const ne_ssl_dname *<function>ne_ssl_cert_issuer</function></funcdef> + <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para>The function <function>ne_ssl_cert_identity</function> + retrieves the <quote>identity</quote> of a certificate; for an + SSL server certificate, this will be the hostname for which the + certificate was issued. In PKI parlance, the identity is the + <emphasis>common name</emphasis> attribute of the distinguished name of + the certificate subject.</para> + + <para>The functions <function>ne_ssl_cert_subject</function> and + <function>ne_ssl_cert_issuer</function> can be used to access the + objects representing the distinguished name of the subject and of + the issuer of a certificate, respectively.</para> + + <para>If a certificate object is part of a certificate chain, then + <function>ne_ssl_cert_signedby</function> can be used to find the + certificate which signed a particular certificate. For a + self-signed certificate or a certificate for which the full chain + is not available, this function will return &null;.</para> + + </refsect1> + + <refsect1> + <title>Return value</title> + + <para><function>ne_ssl_cert_issuer</function> and + <function>ne_ssl_cert_subject</function> are guaranteed to never + return &null;. <function>ne_ssl_cert_identity</function> may + return &null; if the certificate has no specific + <quote>identity</quote>. <function>ne_ssl_cert_signedby</function> + may return &null; as covered above.</para> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para>The following function could be used to display information + about a given certificate:</para> + + <programlisting>void dump_cert(const ne_ssl_certificate *cert) { + const char *id = ne_ssl_cert_identity(cert); + char *dn; + + if (id) + printf("Certificate was issued for '%s'.\n", id); + + dn = ne_ssl_readable_dname(ne_ssl_cert_subject(cert)); + printf("Subject: %s\n", dn); + free(dn); + + dn = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert)); + printf("Issuer: %s\n", dn); + free(dn); +}</programlisting> + + </refsect1> + + <refsect1> + <title>See also</title> + + <para><xref linkend="ne_ssl_cert_cmp"/>, <xref linkend="ne_ssl_readable_dname"/></para> + </refsect1> + +</refentry> + diff --git a/doc/ref/sslcert2.xml b/doc/ref/sslcert2.xml new file mode 100644 index 0000000..216d294 --- /dev/null +++ b/doc/ref/sslcert2.xml @@ -0,0 +1,49 @@ +<refentry id="refsslcert2"> + + <refmeta> + <refentrytitle>ne_ssl_cert_cmp</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname id="ne_ssl_cert_cmp">ne_ssl_cert_cmp</refname> + <refname id="ne_ssl_cert_free">ne_ssl_cert_free</refname> + <refpurpose>functions to operate on certificate objects</refpurpose> + </refnamediv> + + <refsynopsisdiv> + + <funcsynopsis> + + <funcsynopsisinfo>#include <ne_header.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>ne_ssl_cert_cmp</function></funcdef> + <paramdef>const ne_ssl_certificate *<parameter>c1</parameter></paramdef> + <paramdef>const ne_ssl_certificate *<parameter>c2</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>void <function>ne_ssl_cert_free</function></funcdef> + <paramdef>ne_ssl_certificate *<parameter>cert</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para>The <function>ne_ssl_cert_cmp</function> function can be + used to compare two certificate objects; it returns zero if they + refer to the same certificate, and non-zero otherwise.</para> + + <para>The <function>ne_ssl_cert_free</function> function can be + used to destroy a certificate object when it is no longer + needed.</para> + + </refsect1> + +</refentry> + diff --git a/doc/ref/sslcertio.xml b/doc/ref/sslcertio.xml new file mode 100644 index 0000000..394045e --- /dev/null +++ b/doc/ref/sslcertio.xml @@ -0,0 +1,95 @@ +<refentry id="refsslcertio"> + + <refmeta> + <refentrytitle>ne_ssl_cert_read</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname id="ne_ssl_cert_read">ne_ssl_cert_read</refname> + <refname id="ne_ssl_cert_write">ne_ssl_cert_write</refname> + <refname id="ne_ssl_cert_import">ne_ssl_cert_import</refname> + <refname id="ne_ssl_cert_export">ne_ssl_cert_export</refname> + <refpurpose>functions to read or write certificates to and from files or strings</refpurpose> + </refnamediv> + + <refsynopsisdiv> + + <funcsynopsis> + + <funcsynopsisinfo>#include <ne_ssl.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>ne_ssl_certificate *<function>ne_ssl_cert_read</function></funcdef> + <paramdef>const char *<parameter>filename</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>ne_ssl_cert_write</function></funcdef> + <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> + <paramdef>const char *<parameter>filename</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>ne_ssl_certificate *<function>ne_ssl_cert_import</function></funcdef> + <paramdef>const char *<parameter>data</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>char *<function>ne_ssl_cert_export</function></funcdef> + <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para>The <function>ne_ssl_cert_write</function> function writes a + certificate to a file using the PEM encoding. The + <function>ne_ssl_cert_export</function> function returns a + base64-encoded &nul;-terminated string representing the + certificate. This string is malloc-allocated and should be + destroyed using <function>free</function> by the caller.</para> + + <para>The <function>ne_ssl_cert_read</function> function reads a + certificate from a PEM-encoded file, and returns a certificate + object. The <function>ne_ssl_cert_import</function> function + returns a certificate object from a base64-encoded string, + <parameter>data</parameter>, as returned by + <function>ne_ssl_cert_export</function>. The certificate object + returned by these functions should be destroyed using <xref + linkend="ne_ssl_cert_free"/> after use.</para> + + + </refsect1> + + <refsect1> + <title>Return value</title> + + <para><function>ne_ssl_cert_read</function> returns &null; if a + certificate could not be read from the file. + <function>ne_ssl_cert_write</function> returns non-zero if the + certificate could not be written to the file. + <function>ne_ssl_cert_export</function> always returns a + &nul;-terminated string, and never &null;. + <function>ne_ssl_cert_import</function> returns &null; if the + string was not a valid base64-encoded certificate.</para> + + </refsect1> + + <refsect1> + <title>Encoding Formats</title> + + <para>The string produced by + <function>ne_ssl_cert_export</function> is the base64 encoding of + the DER representation of the certificate. The file written by + <function>ne_ssl_cert_write</function> uses the PEM format: this + is the base64 encoding of the DER representation with newlines + every 64 characters, and start and end marker lines.</para> + </refsect1> + +</refentry> + diff --git a/doc/ref/ssldname.xml b/doc/ref/ssldname.xml index accc2cb..e1fd454 100644 --- a/doc/ref/ssldname.xml +++ b/doc/ref/ssldname.xml @@ -51,16 +51,23 @@ creates a single-line, human-readable string out of an <refsect1> <title>Return value</title> - <para><function>ne_ssl_readable_dname</function> returns a - <function>malloc</function>-allocated string, and never - NULL.</para> + <para><function>ne_ssl_readable_dname</function> returns a <function>malloc</function>-allocated + string, and never &null;.</para> + + </refsect1> + + <refsect1> + <title>Examples</title> + + <para>See <xref linkend="ne_ssl_cert_subject"/> for an example + use of <function>ne_ssl_readable_dname</function>.</para> </refsect1> <refsect1> <title>See also</title> - <para><xref linkend="ne_ssl_certificate"/></para> + <para><xref linkend="ne_ssl_cert_subject"/></para> </refsect1> </refentry> diff --git a/doc/ref/ssltrust.xml b/doc/ref/ssltrust.xml new file mode 100644 index 0000000..5a22f50 --- /dev/null +++ b/doc/ref/ssltrust.xml @@ -0,0 +1,72 @@ + <refentry id="refsslca"> + + <refmeta> + <refentrytitle>ne_ssl_trust_cert</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname id="ne_ssl_trust_cert">ne_ssl_trust_cert</refname> + <refname id="ne_ssl_trust_default_ca">ne_ssl_trust_default_ca</refname> + <refpurpose>functions to indicate that certificates are trusted</refpurpose> + </refnamediv> + + <refsynopsisdiv> + + <funcsynopsis> + + <funcsynopsisinfo>#include <ne_session.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>void <function>ne_ssl_trust_cert</function></funcdef> + <paramdef>ne_session *<parameter>session</parameter></paramdef> + <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>void <function>ne_ssl_trust_default_ca</function></funcdef> + <paramdef>ne_session *<parameter>session</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para>To indicate that a given certificate is trusted by the +user, the certificate object can be passed to +<function>ne_ssl_trust_cert</function>. The certificate object is +duplicated internally and can subequently be destroyed.</para> + + <para>The SSL library in use by &neon; may include a default +set of CA certificates; calling the +<function>ne_ssl_trust_default_ca</function> function will indicate +that these CAs are trusted by the user.</para> + + </refsect1> + + <refsect1> + <title>Examples</title> + + <para>Load the CA certificate stored in <filename>/path/to/cacert.pem</filename>:</para> + <programlisting>&egsess; +ne_ssl_certificate *cert = ne_ssl_cert_read("/path/to/cacert.pem"); + +if (cert) { + ne_ssl_trust_cert(sess, cert); + ne_ssl_cert_free(cert); +} else { + printf("Could not load CA cert: %s\n", ne_get_error(sess)); +}</programlisting> + </refsect1> + + <refsect1> + <title>See also</title> + + <para><xref linkend="ne_ssl_cert_read"/>, <xref + linkend="ne_ssl_cert_import"/>, <xref + linkend="ne_ssl_cert_free"/></para> </refsect1> + + </refentry> diff --git a/doc/ref/sslvfy.xml b/doc/ref/sslvfy.xml index 98f0054..b044d5b 100644 --- a/doc/ref/sslvfy.xml +++ b/doc/ref/sslvfy.xml @@ -1,4 +1,4 @@ - <refentry id="refsslvfy"> + <refentry id="refsslvfy"> <!-- -*- xml-mode -*- --> <refmeta> <refentrytitle>ne_ssl_set_verify</refentrytitle> @@ -19,7 +19,7 @@ <!-- hard to put data type declarations here --> <funcprototype> - <funcdef>typedef int (*<function>ne_ssl_verify_fn</function>)</funcdef> + <funcdef>typedef int <function>ne_ssl_verify_fn</function></funcdef> <paramdef>void *<parameter>userdata</parameter></paramdef> <paramdef>int <parameter>failures</parameter></paramdef> <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef> @@ -44,7 +44,7 @@ callback can be registered using <function>ne_ssl_set_verify</function>. If such a callback is not registered, when a connection is established to an SSL server which does not present a certificate signed by a trusted CA (see <xref -linkend="ne_ssl_load_ca"/>), or if the certificate presented is invalid in +linkend="ne_ssl_trust_cert"/>), or if the certificate presented is invalid in some way, the connection will fail.</para> <para>When the callback is invoked, the @@ -54,35 +54,42 @@ is equal to the bit-wise OR of one or more of the following constants (and is guaranteed to be non-zero):</para> <variablelist> - <varlistentry><term><filename>NE_SSL_NOTYETVALID</filename></term> + <varlistentry><term><constant>NE_SSL_NOTYETVALID</constant></term> <listitem> - <para>The certificate is not yet valid.</para> + <simpara>The certificate is not yet valid.</simpara> </listitem> </varlistentry> - <varlistentry><term><filename>NE_SSL_EXPIRED</filename></term> + <varlistentry><term><constant>NE_SSL_EXPIRED</constant></term> <listitem> - <para>The certificate has expired.</para> + <simpara>The certificate has expired.</simpara> </listitem> </varlistentry> - <varlistentry><term><filename>NE_SSL_CNMISMATCH</filename></term> + <varlistentry><term><constant>NE_SSL_IDMISMATCH</constant></term> <listitem> - <para>The hostname used for the session does not match -the hostname to which the certificate was issued: this could mean that -the connection has been intercepted.</para> + <simpara>The hostname used for the session does not match +the hostname to which the certificate was issued.</simpara> </listitem> </varlistentry> - <varlistentry><term><filename>NE_SSL_UNKNOWNCA</filename></term> + <varlistentry><term><constant>NE_SSL_UNTRUSTED</constant></term> <listitem> - <para>The Certificate Authority which signed the certificate -is not trusted.</para> + <simpara>The Certificate Authority which signed the certificate +is not trusted.</simpara> </listitem> </varlistentry> </variablelist> + <para>Note that if either of the + <constant>NE_SSL_IDMISMATCH</constant> or + <constant>NE_SSL_UNTRUSTED</constant> failures is given, the + connection may have been intercepted by a third party, and + must not be presumed to be <quote>secure</quote>.</para> + <para>The <parameter>cert</parameter> parameter passed to the -callback describes the certificate which was presented by the server, -see <xref linkend="ne_ssl_certificate"/> for more details. The certificate -object given is only valid until the callback returns.</para> +callback represents the certificate which was presented by the server. +If the server presented a chain of certificates, the chain can be +accessed using <xref linkend="ne_ssl_cert_signedby"/>. The +<parameter>cert</parameter> object given is not valid after the +callback returns.</para> </refsect1> @@ -97,35 +104,51 @@ which case, the connection will fail).</para> <refsect1> <title>Examples</title> - <para>Manual certificate verification:</para> + <para>The following code implements an example verification + callback, using the <function>dump_cert</function> function + from <xref linkend="ne_ssl_cert_subject"/> to display + certification information. Notice that the hostname of the + server used for the session is passed as the + <parameter>userdata</parameter> parameter to the + callback.</para> + <programlisting> static int my_verify(void *userdata, int failures, const ne_ssl_certificate *cert) { - /* leak the return values of ne_ssl_readable_dname for simplicity! */ - printf("Issuer: %s\n", ne_ssl_readable_dname(cert->issuer); - printf("Subject: %s\n", ne_ssl_readable_dname(cert->subject); - if (failures & NE_SSL_CNMISMATCH) { - printf("Server certificate was issued to `%s'; " - "connection may have been intercepted!\n", - cert->subject->commonName); - } - if (failures & NE_SSL_EXPIRED) { - printf("Server certificate expired on %s!", cert->until); - } - /* ... check for other failures ... */ - if (prompt_user()) - return 1; /* fail verification */ - else - return 0; /* trust certificate */ + const char *hostname = userdata; + + dump_cert(cert); + + puts("Certificate verification failed - the connection may have been " + "intercepted by a third party!"); + + if (failures & NE_SSL_IDMISMATCH) { + const char *id = ne_ssl_cert_identity(cert); + if (id) + printf("Server certificate was issued to '%s' not '%s'.\n", + id, hostname); + else + printf("The certificate was not issued for '%s'\n", hostname); + } + + if (failures & NE_SSL_UNTRUSTED) + puts("The certificate is not signed by a trusted Certificate Authority."); + + /* ... check for validity failures ... */ + + if (prompt_user()) + return 1; /* fail verification */ + else + return 0; /* trust the certificate anyway */ } int main(...) { - ne_session *sess = ne_session_create("https", "some.host.name", 443); - ne_ssl_set_verify(sess, my_verify, NULL); - ... + ne_session *sess = ne_session_create("https", "some.host.name", 443); + ne_ssl_set_verify(sess, my_verify, "some.host.name"); + ... }</programlisting> </refsect1> @@ -133,8 +156,8 @@ main(...) <refsect1> <title>See also</title> - <para><xref linkend="ne_ssl_certificate"/>, <xref linkend="ne_ssl_load_ca"/>, - <xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_readable_dname"/></para> + <para><xref linkend="ne_ssl_trust_cert"/>, <xref + linkend="ne_ssl_readable_dname"/>, <xref linkend="ne_ssl_cert_subject"/></para> </refsect1> - </refentry> + </refentry> diff --git a/doc/ref/status.xml b/doc/ref/status.xml index 56616ea..fbc38b7 100644 --- a/doc/ref/status.xml +++ b/doc/ref/status.xml @@ -32,8 +32,12 @@ The <structfield>major_version</structfield> and supported by the server issuing the response. The <structfield>code</structfield> field gives the status code of the result (lying between 100 and 999 inclusive), and the -<structfield>klass</structfield> field gives the class, which is equal -to the most significant digit of the status.</para> +<structfield>klass</structfield> field gives the +class<footnote><para>the field is named <quote>klass</quote> not +<quote>class</quote> so that the header can be used from a C++ +program, in which <quote>class</quote> is a reserved +word)</para></footnote>, which is equal to the most significant digit +of the status.</para> <para>There are five classes of HTTP status code defined by RFC2616:</para> @@ -41,28 +45,28 @@ to the most significant digit of the status.</para> <variablelist> <varlistentry> <term><literal>1xx</literal></term> - <listitem><para>Informational response.</para></listitem> + <listitem><simpara>Informational response.</simpara></listitem> </varlistentry> <varlistentry> <term><literal>2xx</literal></term> - <listitem><para>Success: the operation was successful</para></listitem> + <listitem><simpara>Success: the operation was successful</simpara></listitem> </varlistentry> <varlistentry> <term><literal>3xx</literal></term> - <listitem><para>Redirection</para></listitem> + <listitem><simpara>Redirection</simpara></listitem> </varlistentry> <varlistentry> - <term><literal>4xx</literal></term> <listitem><para>Client + <term><literal>4xx</literal></term> <listitem><simpara>Client error: the request made was incorrect in some - manner.</para></listitem> + manner.</simpara></listitem> </varlistentry> <varlistentry> <term><literal>5xx</literal></term> - <listitem><para>Server error</para></listitem> + <listitem><simpara>Server error</simpara></listitem> </varlistentry> </variablelist> diff --git a/doc/ssl.xml b/doc/ssl.xml new file mode 100644 index 0000000..5581373 --- /dev/null +++ b/doc/ssl.xml @@ -0,0 +1,24 @@ +<sect1 id="ssl"> <!-- -*- xml -*- --> + + <title>Secure connections: HTTP over SSL</title> + + <para>This section gives an introduction to SSL. The text is + inspired by <xref linkend="bib.ssltls"/>.</para> + + <para>&neon; supports the use of HTTP over SSL<footnote><para>The +term <quote>SSL</quote> is used throughout this section to refer in +general to both the SSL protocol developed by Netscape and its +successor TLS, as adopted by the IETF.</para></footnote> to implement +<firstterm>secure connections</firstterm>. A secure connection in +this context means a connection which has +<emphasis>integrity</emphasis>, <emphasis>secrecy</emphasis> and is +<emphasis>authenticated</emphasis>. Applications must go to some +effort to correctly support secure connections—an application +based on &neon; does not magically become <quote>secure</quote> simply +by flicking a switch and enabling the use of SSL.</para> + +<!-- SSL: integrity, secrecy, authentication. --> + +<!-- what is a certificate --> + +</sect1> diff --git a/doc/xml.xml b/doc/xml.xml index c001073..4292eb1 100644 --- a/doc/xml.xml +++ b/doc/xml.xml @@ -10,8 +10,7 @@ url="http://www.saxproject.org/">SAX</ulink> API used by XML parsers, with an additional abstraction, <firstterm>stacked SAX handlers</firstterm>, and also giving consistent <ulink - url="http://www.w3.org/TR/REC-xml-names">XML Namespace - support</ulink>.</para> + url="http://www.w3.org/TR/REC-xml-names">XML Namespace</ulink> support.</para> <sect2 id="xml-sax"> <title>Introduction to SAX</title> diff --git a/macros/ChangeLog b/macros/ChangeLog index 5a5ac37..b0a789c 100644 --- a/macros/ChangeLog +++ b/macros/ChangeLog @@ -1,3 +1,11 @@ +Thu Sep 4 21:29:06 2003 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for netinet/tcp.h. + +Wed Jul 23 21:17:40 2003 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NEON_GSSAPI): New macro. + Mon Apr 21 18:24:12 2003 Joe Orton <joe@manyfish.co.uk> * neon-xml-parser.m4 (HAVE_EXPAT): Fail if --with-expat is given diff --git a/macros/neon.m4 b/macros/neon.m4 index 6b0cee6..f18c560 100644 --- a/macros/neon.m4 +++ b/macros/neon.m4 @@ -122,7 +122,7 @@ AC_DEFUN([NEON_VERSIONS], [ # Define the current versions. NEON_VERSION_MAJOR=0 NEON_VERSION_MINOR=24 -NEON_VERSION_RELEASE=0 +NEON_VERSION_RELEASE=1 NEON_VERSION_TAG= NEON_VERSION="${NEON_VERSION_MAJOR}.${NEON_VERSION_MINOR}.${NEON_VERSION_RELEASE}${NEON_VERSION_TAG}" @@ -482,7 +482,7 @@ dnl Is strerror_r present; if so, which variant AC_REQUIRE([AC_FUNC_STRERROR_R]) AC_CHECK_HEADERS([strings.h sys/time.h limits.h sys/select.h arpa/inet.h \ - signal.h sys/socket.h netinet/in.h netdb.h]) + signal.h sys/socket.h netinet/in.h netinet/tcp.h netdb.h]) AC_REQUIRE([NE_SNPRINTF]) @@ -532,6 +532,7 @@ fi NEON_SSL() NEON_SOCKS() +NEON_GSSAPI() AC_SUBST(NEON_CFLAGS) AC_SUBST(NEON_LIBS) @@ -766,6 +767,30 @@ esac AC_SUBST(NEON_SUPPORTS_SSL) ]) +dnl Check for Kerberos installation +AC_DEFUN([NEON_GSSAPI], [ +AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin) +if test "x$KRB5_CONFIG" != "xnone"; then + NEON_LIBS="$NEON_LIBS `${KRB5_CONFIG} --libs gssapi`" + CPPFLAGS="$CPPFLAGS `${KRB5_CONFIG} --cflags gssapi`" + # MIT and Heimdal put gssapi.h in different places + AC_CHECK_HEADERS(gssapi/gssapi.h gssapi.h, [ + NE_CHECK_FUNCS(gss_init_sec_context, [ + AC_MSG_NOTICE([GSSAPI authentication support enabled]) + AC_DEFINE(HAVE_GSSAPI, 1, [Define if GSSAPI support is enabled]) + # MIT Kerberos lacks GSS_C_NT_HOSTBASED_SERVICE + AC_CHECK_DECL([GSS_C_NT_HOSTBASED_SERVICE],, + [AC_DEFINE([GSS_C_NT_HOSTBASED_SERVICE], gss_nt_service_name, + [Define if GSS_C_NT_HOSTBASED_SERVICE is not defined otherwise])], + [#ifdef HAVE_GSSAPI_GSSAPI_H +#include <gssapi/gssapi.h> +#else +#include <gssapi.h> +#endif])]) + break + ]) +fi]) + dnl Adds an --enable-warnings argument to configure to allow enabling dnl compiler warnings AC_DEFUN([NEON_WARNINGS],[ diff --git a/src/.cvsignore b/src/.cvsignore index 5561c9e..6eb5bf3 100644 --- a/src/.cvsignore +++ b/src/.cvsignore @@ -13,3 +13,4 @@ checkincl.c *.bb *.da *.bbg +*.[is] diff --git a/src/ChangeLog b/src/ChangeLog index 2893290..3aa1649 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,83 @@ +Thu Sep 4 21:41:38 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c (ne_sock_init): Succeed even if PRNG was not seeded. + +Thu Sep 4 21:33:34 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_session.c (ne_set_useragent): Build and store the entire + User-Agent header field in sess->user_agent. + + * ne_request.c (add_fixed_headers): Adjust accordingly; avoid + unnecessary calls to ne_buffer_*. + +Thu Sep 4 21:27:34 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c: Include netinet/tcp.h. + (ne_sock_connect): Disable the Nagle algorithm; thanks to Jim + Whitehead and Teng Xu for the analysis. + +Thu Sep 4 11:24:04 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_defs.h: Define ssize_t here for Win32. + + * ne_socket.h: Don't define ssize_t here. + +Tue Sep 2 20:20:16 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (auth_challenge): Update to use ne_token not + split_string, patch by Tom Lee <i_am_gnomey@hotmail.com>. + +Wed Jul 30 21:54:38 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_cookies.c (set_cookie_hdl): Fix NULL pointer dereference; + thanks to Markus Mueller <markus-m.mueller@ubs.com>. + +Fri Jul 25 11:05:52 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c (do_connect): On failure to connect, set error + string and call ne_sock_close directly rather than using + aborted(); fix leak of socket structure. + +Wed Jul 23 23:20:42 2003 Joe Orton <joe@manyfish.co.uk> + + Fix SEGV if inflateInit2 fails with Z_MEM_ERROR etc. + + * ne_compress.c (set_zlib_error): New function. + (do_inflate, gz_reader): Use it. + +Wed Jul 23 22:50:50 2003 Joe Orton <joe@manyfish.co.uk> + + Add support for GSS-Negotiate; patch from Risko Gergely and Burjan + Gabor: + + * ne_auth.c [HAVE_GSSAPI]: Include gssapi.h. + (auth_scheme): Add auth_scheme_gssapi. + (auth_session): Add gssapi_token. + (clean_session): Free gssapi_token. + (request_gssapi, get_gss_name, gssapi_challenge): New functions. + (tokenize): Handle challenge with single token. + (auth_challenge): Accept and process a GSS-Negotiate challenge. + (ah_pre_send): Send GSS-Negotiate handshake. + +Wed Jul 23 22:46:28 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_207.c (ne_207_set_response_handlers, + ne_207_set_propstat_handlers): Fix to match declarations (thanks + to Diego Tártara). + +Fri Jun 27 20:30:45 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_openssl.c [OPENSSL_VERSION_NUMBER < 0x0090700fL]: + Fix build against OpenSSL < 0.9.7. + +Sun Jun 22 23:07:45 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_session.c (ne_session_destroy): Replace unnecessary use of + NE_FREE with ne_free. + (set_hostinfo): Don't free hostport/hostinfo here. + (ne_session_proxy): Free existing proxy hostname here if + necessary. + Sat Jun 21 12:58:25 2003 Joe Orton <joe@manyfish.co.uk> * ne_request.c (ne_begin_request): Set or clear is_http11 flag diff --git a/src/ne_207.c b/src/ne_207.c index ad5e6a1..b5676a7 100644 --- a/src/ne_207.c +++ b/src/ne_207.c @@ -78,16 +78,16 @@ static const struct ne_xml_idmap map207[] = { /* Set the callbacks for the parser */ void ne_207_set_response_handlers(ne_207_parser *p, - ne_207_start_response start, - ne_207_end_response end) + ne_207_start_response *start, + ne_207_end_response *end) { p->start_response = start; p->end_response = end; } void ne_207_set_propstat_handlers(ne_207_parser *p, - ne_207_start_propstat start, - ne_207_end_propstat end) + ne_207_start_propstat *start, + ne_207_end_propstat *end) { p->start_propstat = start; p->end_propstat = end; diff --git a/src/ne_auth.c b/src/ne_auth.c index ba0095c..966d0f4 100644 --- a/src/ne_auth.c +++ b/src/ne_auth.c @@ -62,6 +62,15 @@ #include "ne_uri.h" #include "ne_i18n.h" +#ifdef HAVE_GSSAPI +#ifdef HAVE_GSSAPI_GSSAPI_H /* MIT */ +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_generic.h> +#else /* Heimdal. */ +#include <gssapi.h> +#endif +#endif + /* TODO: should remove this eventually. Need it for * ne_pull_request_body. */ #include "ne_private.h" @@ -72,7 +81,8 @@ /* The authentication scheme we are using */ typedef enum { auth_scheme_basic, - auth_scheme_digest + auth_scheme_digest, + auth_scheme_gssapi } auth_scheme; typedef enum { @@ -137,6 +147,10 @@ typedef struct { unsigned int can_handle:1; /* This used for Basic auth */ char *basic; +#ifdef HAVE_GSSAPI + /* This used for GSSAPI auth */ + char *gssapi_token; +#endif /* These all used for Digest auth */ char *realm; char *nonce; @@ -186,6 +200,9 @@ static void clean_session(auth_session *sess) NE_FREE(sess->cnonce); NE_FREE(sess->opaque); NE_FREE(sess->realm); +#ifdef HAVE_GSSAPI + NE_FREE(sess->gssapi_token); +#endif } /* Returns client nonce string. */ @@ -287,6 +304,73 @@ static char *request_basic(auth_session *sess) return ne_concat("Basic ", sess->basic, "\r\n", NULL); } +#ifdef HAVE_GSSAPI +/* Add GSSAPI authentication credentials to a request */ +static char *request_gssapi(auth_session *sess) +{ + return ne_concat("GSS-Negotiate ", sess->gssapi_token, "\r\n", NULL); +} + +static int get_gss_name(gss_name_t *server, auth_session *sess) +{ + int major_status, minor_status; + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + + token.value = ne_concat("khttp@", sess->sess->server.hostname, NULL); + token.length = strlen(token.value); + + major_status = gss_import_name(&minor_status, &token, + GSS_C_NT_HOSTBASED_SERVICE, + server); + return GSS_ERROR(major_status) ? -1 : 0; +} + +/* Examine a GSSAPI auth challenge; returns 0 if a valid challenge, + * else non-zero. */ +static int +gssapi_challenge(auth_session *sess, struct auth_challenge *parms) +{ + gss_ctx_id_t context; + gss_name_t server_name; + int major_status, minor_status; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + + clean_session(sess); + + if (get_gss_name(&server_name, sess)) + return -1; + + major_status = gss_init_sec_context(&minor_status, + GSS_C_NO_CREDENTIAL, + &context, + server_name, + GSS_C_NO_OID, + GSS_C_DELEG_FLAG, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + NULL, + &output_token, + NULL, + NULL); + if (GSS_ERROR(major_status)) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gss_init_sec_context failed.\n"); + return -1; + } + + if (output_token.length == 0) + return -1; + + sess->gssapi_token = ne_base64(output_token.value, output_token.length); + + NE_DEBUG(NE_DBG_HTTPAUTH, + "Base64 encoded GSSAPI challenge: %s.\n", sess->gssapi_token); + sess->scheme = auth_scheme_gssapi; + return 0; +} +#endif + /* Examine a digest challenge: return 0 if it is a valid Digest challenge, * else non-zero. */ static int digest_challenge(auth_session *sess, struct auth_challenge *parms) @@ -563,6 +647,10 @@ static int tokenize(char **hdr, char **key, char **value, int ischall) } } while (*++pnt != '\0'); + if (state == BEFORE_EQ && ischall && *key != NULL) { + *value = NULL; + } + *hdr = pnt; /* End of string: */ @@ -751,6 +839,11 @@ static int auth_challenge(auth_session *sess, const char *value) } else if (strcasecmp(key, "digest") == 0) { NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n"); chall->scheme = auth_scheme_digest; +#ifdef HAVE_GSSAPI + } else if (strcasecmp(key, "gss-negotiate") == 0) { + NE_DEBUG(NE_DBG_HTTPAUTH, "GSSAPI scheme.\n"); + chall->scheme = auth_scheme_gssapi; +#endif } else { NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n"); ne_free(chall); @@ -788,18 +881,16 @@ static int auth_challenge(auth_session *sess, const char *value) chall->alg = auth_alg_unknown; } } else if (strcasecmp(key, "qop") == 0) { - char **qops; - int qop; - qops = split_string(val, ',', NULL, " \r\n\t"); - chall->got_qop = 1; - for (qop = 0; qops[qop] != NULL; qop++) { - if (strcasecmp(qops[qop], "auth") == 0) { - chall->qop_auth = 1; - } else if (strcasecmp(qops[qop], "auth-int") == 0) { - chall->qop_auth_int = 1; - } - } - split_string_free(qops); + /* iterate over each token in the value */ + do { + const char *tok = ne_shave(ne_token(&val, ','), " \t"); + + if (strcasecmp(tok, "auth") == 0) { + chall->qop_auth = 1; + } else if (strcasecmp(tok, "auth-int") == 0 ) { + chall->qop_auth_int = 1; + } + } while (val); } } @@ -813,17 +904,31 @@ static int auth_challenge(auth_session *sess, const char *value) success = 0; - NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for Digest challenges.\n"); - - /* Try a digest challenge */ +#ifdef HAVE_GSSAPI + NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for GSSAPI.\n"); + /* Try a GSSAPI challenge */ for (chall = challenges; chall != NULL; chall = chall->next) { - if (chall->scheme == auth_scheme_digest) { - if (!digest_challenge(sess, chall)) { + if (chall->scheme == auth_scheme_gssapi) { + if (!gssapi_challenge(sess, chall)) { success = 1; break; } } } +#endif + + /* Try a digest challenge */ + if (!success) { + NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for Digest challenges.\n"); + for (chall = challenges; chall != NULL; chall = chall->next) { + if (chall->scheme == auth_scheme_digest) { + if (!digest_challenge(sess, chall)) { + success = 1; + break; + } + } + } + } if (!success) { NE_DEBUG(NE_DBG_HTTPAUTH, @@ -921,6 +1026,11 @@ static void ah_pre_send(ne_request *r, void *cookie, ne_buffer *request) case auth_scheme_digest: value = request_digest(sess, req); break; +#ifdef HAVE_GSSAPI + case auth_scheme_gssapi: + value = request_gssapi(sess); + break; +#endif default: value = NULL; break; diff --git a/src/ne_compress.c b/src/ne_compress.c index b84b46d..56db5a5 100644 --- a/src/ne_compress.c +++ b/src/ne_compress.c @@ -31,6 +31,7 @@ #include "ne_request.h" #include "ne_compress.h" #include "ne_utils.h" +#include "ne_i18n.h" #ifdef NEON_ZLIB @@ -170,6 +171,26 @@ static void process_footer(ne_decompress *ctx, } } +/* A zlib function failed with 'code'; set the session error string + * appropriately. */ +static void set_zlib_error(ne_decompress *ctx, const char *msg, int code) +{ + if (ctx->zstr.msg) + ne_set_error(ctx->session, _("%s: %s"), msg, ctx->zstr.msg); + else { + const char *err; + switch (code) { + case Z_STREAM_ERROR: err = "stream error"; break; + case Z_DATA_ERROR: err = "data corrupt"; break; + case Z_MEM_ERROR: err = "out of memory"; break; + case Z_BUF_ERROR: err = "buffer error"; break; + case Z_VERSION_ERROR: err = "library version mismatch"; break; + default: err = "unknown error"; break; + } + ne_set_error(ctx->session, _("%s: %s (code %d)"), msg, err, code); + } +} + /* Inflate response buffer 'buf' of length 'len'. */ static void do_inflate(ne_decompress *ctx, const char *buf, size_t len) { @@ -212,9 +233,7 @@ static void do_inflate(ne_decompress *ctx, const char *buf, size_t len) process_footer(ctx, ctx->zstr.next_in, ctx->zstr.avail_in); } else if (ret != Z_OK) { ctx->state = NE_Z_ERROR; - ne_set_error(ctx->session, "Error reading compressed data."); - NE_DEBUG(NE_DBG_HTTP, "compress: inflate failed (%d): %s\n", - ret, ctx->zstr.msg?ctx->zstr.msg:"(no message)"); + set_zlib_error(ctx, _("Could not inflate data"), ret); } } @@ -248,16 +267,15 @@ static void gz_reader(void *ud, const char *buf, size_t len) case NE_Z_BEFORE_DATA: /* work out whether this is a compressed response or not. */ if (ctx->enchdr && strcasecmp(ctx->enchdr, "gzip") == 0) { + int ret; NE_DEBUG(NE_DBG_HTTP, "compress: got gzipped stream.\n"); - /* This is the magic bit: using plain inflateInit() - * doesn't work, and this does, but I have no idea why.. - * Google showed me the way. */ - if (inflateInit2(&ctx->zstr, -MAX_WBITS) != Z_OK) { - ne_set_error(ctx->session, ctx->zstr.msg); - ctx->state = NE_Z_ERROR; - return; - } + /* inflateInit2() works here where inflateInit() doesn't. */ + ret = inflateInit2(&ctx->zstr, -MAX_WBITS); + if (ret != Z_OK) { + set_zlib_error(ctx, _("Could not initialize zlib"), ret); + return; + } ctx->zstrinit = 1; } else { @@ -291,8 +309,6 @@ static void gz_reader(void *ud, const char *buf, size_t len) len -= count; switch (parse_header(ctx)) { - case HDR_ERROR: - return; case HDR_EXTENDED: if (len == 0) return; @@ -301,6 +317,7 @@ static void gz_reader(void *ud, const char *buf, size_t len) if (len > 0) { do_inflate(ctx, buf, len); } + default: return; } diff --git a/src/ne_cookies.c b/src/ne_cookies.c index f84f2fa..1cc1a89 100644 --- a/src/ne_cookies.c +++ b/src/ne_cookies.c @@ -81,6 +81,7 @@ static void set_cookie_hdl(void *userdata, const char *value) cook->value = ne_strdup(pairs[1]); for (n = 2; pairs[n] != NULL; n+=2) { + if (!pairs[n+1]) continue; NE_DEBUG(NE_DBG_HTTP, "Cookie parm %s=%s\n", pairs[n], pairs[n+1]); if (strcasecmp(pairs[n], "path") == 0) { cook->path = ne_strdup(pairs[n+1]); diff --git a/src/ne_defs.h b/src/ne_defs.h index f029edf..d13a9bf 100644 --- a/src/ne_defs.h +++ b/src/ne_defs.h @@ -1,3 +1,23 @@ +/* + Standard definitions for neon headers + Copyright (C) 2003, Joe Orton <joe@manyfish.co.uk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA + +*/ #undef BEGIN_NEON_DECLS #undef END_NEON_DECLS @@ -8,3 +28,8 @@ # define BEGIN_NEON_DECLS /* empty */ # define END_NEON_DECLS /* empty */ #endif + +/* define ssize_t for Win32 */ +#if defined(WIN32) && !defined(ssize_t) +#define ssize_t int +#endif diff --git a/src/ne_openssl.c b/src/ne_openssl.c index 5d21ef1..359f7a4 100644 --- a/src/ne_openssl.c +++ b/src/ne_openssl.c @@ -45,6 +45,14 @@ #include "ne_private.h" #include "ne_privssl.h" +/* OpenSSL 0.9.6 compatibility */ +#if OPENSSL_VERSION_NUMBER < 0x0090700fL +#define PKCS12_unpack_authsafes M_PKCS12_unpack_authsafes +#define PKCS12_unpack_p7data M_PKCS12_unpack_p7data +/* cast away lack of const-ness */ +#define OBJ_cmp(a,b) OBJ_cmp((ASN1_OBJECT *)(a), (ASN1_OBJECT *)(b)) +#endif + struct ne_ssl_dname_s { X509_NAME *dn; }; diff --git a/src/ne_private.h b/src/ne_private.h index 964800d..aad98f4 100644 --- a/src/ne_private.h +++ b/src/ne_private.h @@ -86,7 +86,7 @@ struct ne_session_s { struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks; struct hook *destroy_req_hooks, *destroy_sess_hooks, *private; - char *user_agent; /* full User-Agent string */ + char *user_agent; /* full User-Agent: header field */ #ifdef NEON_SSL ne_ssl_client_cert *client_cert; diff --git a/src/ne_request.c b/src/ne_request.c index 0d39214..85287de 100644 --- a/src/ne_request.c +++ b/src/ne_request.c @@ -431,23 +431,23 @@ static int send_request_body(ne_request *req) static void add_fixed_headers(ne_request *req) { if (req->session->user_agent) { - ne_buffer_concat(req->headers, - "User-Agent: ", req->session->user_agent, EOL, NULL); + ne_buffer_zappend(req->headers, req->session->user_agent); } - /* Send Connection: Keep-Alive for pre-1.1 origin servers, so we - * might get a persistent connection. 2068 sec 19.7.1 says we MUST - * NOT do this for proxies, though. So we don't. Note that on the - * first request on any session, we don't know whether the server - * is 1.1 compliant, so we presume that it is not. */ + + /* Send Connection: Keep-Alive to pre-1.1 origin servers to try + * harder to get a persistent connection, except if using a proxy + * as per 2068 sec 19.7.1. Always add TE: trailers since those + * are understood. */ if (!req->session->is_http11 && !req->session->use_proxy) { - ne_buffer_zappend(req->headers, "Keep-Alive: " EOL); - ne_buffer_zappend(req->headers, "Connection: TE, Keep-Alive" EOL); + ne_buffer_zappend(req->headers, + "Keep-Alive: " EOL + "Connection: TE, Keep-Alive" EOL + "TE: trailers" EOL); } else { - ne_buffer_zappend(req->headers, "Connection: TE" EOL); + ne_buffer_zappend(req->headers, + "Connection: TE" EOL + "TE: trailers" EOL); } - /* We send TE: trailers since we understand trailers in the chunked - * response. */ - ne_buffer_zappend(req->headers, "TE: trailers" EOL); } @@ -1338,7 +1338,8 @@ static int do_connect(ne_request *req, struct host_info *host, const char *err) (host->current = ne_addr_next(host->address)) != NULL); if (ret) { - aborted(req, err, ret); + ne_set_error(sess, "%s: %s", err, ne_sock_error(sess->socket)); + ne_sock_close(sess->socket); return NE_CONNECT; } diff --git a/src/ne_session.c b/src/ne_session.c index fe71079..04b777e 100644 --- a/src/ne_session.c +++ b/src/ne_session.c @@ -72,13 +72,13 @@ void ne_session_destroy(ne_session *sess) destroy_hooks(sess->destroy_sess_hooks); destroy_hooks(sess->private); - NE_FREE(sess->server.hostname); - NE_FREE(sess->server.hostport); + ne_free(sess->scheme); + ne_free(sess->server.hostname); + ne_free(sess->server.hostport); if (sess->server.address) ne_addr_destroy(sess->server.address); if (sess->proxy.address) ne_addr_destroy(sess->proxy.address); - NE_FREE(sess->proxy.hostname); - NE_FREE(sess->scheme); - NE_FREE(sess->user_agent); + if (sess->proxy.hostname) ne_free(sess->proxy.hostname); + if (sess->user_agent) ne_free(sess->user_agent); if (sess->connected) { ne_close_connection(sess); @@ -118,8 +118,6 @@ static void set_hostport(struct host_info *host, unsigned int defaultport) static void set_hostinfo(struct host_info *info, const char *hostname, unsigned int port) { - NE_FREE(info->hostport); - NE_FREE(info->hostname); info->hostname = ne_strdup(hostname); info->port = port; } @@ -158,6 +156,7 @@ void ne_session_proxy(ne_session *sess, const char *hostname, unsigned int port) { sess->use_proxy = 1; + if (sess->proxy.hostname) ne_free(sess->proxy.hostname); set_hostinfo(&sess->proxy, hostname, port); } @@ -204,13 +203,19 @@ void ne_set_read_timeout(ne_session *sess, int timeout) sess->rdtimeout = timeout; } -#define AGENT " neon/" NEON_VERSION +#define UAHDR "User-Agent: " +#define AGENT " neon/" NEON_VERSION "\r\n" void ne_set_useragent(ne_session *sess, const char *token) { if (sess->user_agent) ne_free(sess->user_agent); - sess->user_agent = ne_malloc(sizeof AGENT + strlen(token)); - strcat(strcpy(sess->user_agent, token), AGENT); + sess->user_agent = ne_malloc(strlen(UAHDR) + strlen(AGENT) + + strlen(token) + 1); +#ifdef HAVE_STPCPY + strcpy(stpcpy(stpcpy(sess->user_agent, UAHDR), token), AGENT); +#else + strcat(strcat(strcpy(sess->user_agent, UAHDR), token), AGENT); +#endif } const char *ne_get_server_hostport(ne_session *sess) diff --git a/src/ne_socket.c b/src/ne_socket.c index f67e233..fd8afdb 100644 --- a/src/ne_socket.c +++ b/src/ne_socket.c @@ -50,6 +50,9 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> +#endif #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif @@ -318,10 +321,9 @@ int ne_sock_init(void) #ifdef NEON_SSL if (init_ssl()) { NE_DEBUG(NE_DBG_SOCKET, "SSL initialization failed; lacking PRNG?\n"); - init_result = -1; - return -1; + } else { + prng_seeded = 1; } - prng_seeded = 1; #endif init_result = 1; @@ -859,6 +861,14 @@ int ne_sock_connect(ne_socket *sock, return -1; } +#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) + { /* Disable the Nagle algorithm; better to add write buffering + * instead of doing this. */ + int flag = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof flag); + } +#endif + if (raw_connect(fd, addr, ntohs(port))) { set_strerror(sock, ne_errno); ne_close(fd); diff --git a/src/ne_socket.h b/src/ne_socket.h index 58f45ff..4da0c0c 100644 --- a/src/ne_socket.h +++ b/src/ne_socket.h @@ -1,6 +1,6 @@ /* socket handling interface - Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -29,11 +29,6 @@ BEGIN_NEON_DECLS -/* define ssize_t for Win32 */ -#if defined(WIN32) && !defined(ssize_t) -#define ssize_t int -#endif - #define NE_SOCK_ERROR (-1) /* Read/Write timed out */ #define NE_SOCK_TIMEOUT (-2) diff --git a/src/ne_utils.c b/src/ne_utils.c index 84f7470..e840777 100644 --- a/src/ne_utils.c +++ b/src/ne_utils.c @@ -135,41 +135,33 @@ int ne_parse_statusline(const char *status_line, ne_status *st) const char *part; int major, minor, status_code, klass; - /* Check they're speaking the right language */ - status_line = strstr(status_line, "HTTP/"); - if (status_line == NULL) { - return -1; - } - - /* And find out which dialect of this peculiar language - * they can talk... */ - major = 0; - minor = 0; - /* Note, we're good children, and accept leading zero's on the - * version numbers */ - for (part = status_line + 5; *part != '\0' && isdigit(*part); part++) { + /* skip leading garbage if any. */ + part = strstr(status_line, "HTTP/"); + if (part == NULL) return -1; + + minor = major = 0; + + /* Parse version string, skipping leading zeroes. */ + for (part += 5; *part != '\0' && isdigit(*part); part++) major = major*10 + (*part-'0'); - } - if (*part != '.') { - return -1; - } - for (part++ ; *part != '\0' && isdigit(*part); part++) { + + if (*part++ != '.') return -1; + + for (;*part != '\0' && isdigit(*part); part++) minor = minor*10 + (*part-'0'); - } - if (*part != ' ') { - return -1; - } + + if (*part != ' ') return -1; + /* Skip any spaces */ - for (; *part == ' ' ; part++) /* noop */; - /* Now for the Status-Code. part now points at the first Y in - * "HTTP/x.x YYY". We want value of YYY... could use atoi, but - * probably quicker this way. */ + for (; *part == ' '; part++) /* noop */; + + /* Parse the Status-Code; part now points at the first Y in + * "HTTP/x.x YYY". */ if (!isdigit(part[0]) || !isdigit(part[1]) || !isdigit(part[2]) || - (part[3] != '\0' && part[3] != ' ')) { - return -1; - } + (part[3] != '\0' && part[3] != ' ')) return -1; status_code = 100*(part[0]-'0') + 10*(part[1]-'0') + (part[2]-'0'); klass = part[0]-'0'; + /* Skip whitespace between status-code and reason-phrase */ for (part+=3; *part == ' ' || *part == '\t'; part++) /* noop */; diff --git a/test/.cvsignore b/test/.cvsignore index fb288d0..acc3a9a 100644 --- a/test/.cvsignore +++ b/test/.cvsignore @@ -7,7 +7,9 @@ server regress compress *.gz +*.lo *.tmp +.libs acl auth lock @@ -39,3 +41,5 @@ chain.pem *.p12 client.* output.pem +libtest.* +clog diff --git a/test/ChangeLog b/test/ChangeLog index a32791c..2ea156c 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,16 @@ +Sat Aug 30 18:59:24 2003 Joe Orton <joe@manyfish.co.uk> + + * Makefile.in: Rewrite to use libtool to build object files and + libtest. + + * run.sh: Don't set LD_LIBRARY_PATH. + +Wed Jul 23 23:25:39 2003 Joe Orton <joe@manyfish.co.uk> + + * compress.c (do_fetch): Check for response truncation + for success case. + (fail_corrupt1, fail_corrupt2): New tests. + Sat Jun 21 12:59:49 2003 Joe Orton <joe@manyfish.co.uk> * request.c (versions): Fix and enable test. diff --git a/test/Makefile.in b/test/Makefile.in index eb0da59..be5a6ef 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -3,7 +3,7 @@ SHELL = @SHELL@ CPPFLAGS = @CPPFLAGS@ -I. -I$(top_srcdir)/src -I$(top_srcdir)/test/common CFLAGS = @CFLAGS@ @NEON_CFLAGS@ -LDFLAGS = -L. @LDFLAGS@ -L$(top_srcdir)/src -L../src/.libs +LDFLAGS = @LDFLAGS@ DEFS = @DEFS@ top_builddir = .. @@ -14,7 +14,7 @@ VPATH = @srcdir@ AR = ar RANLIB = @RANLIB@ -LIBS = -ltest -lneon @NEON_LIBS@ +LIBS = $(LIBTEST) CC = @CC@ OPENSSL = @OPENSSL@ @@ -22,22 +22,33 @@ HELPERS = @HELPERS@ BASIC_TESTS = uri-tests util-tests string-tests session socket \ request auth basic stubs redirect ZLIB_TESTS = compress -ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz +ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz corrupt1.gz corrupt2.gz DAV_TESTS = xml acl props lock SSL_TESTS = socket-ssl ssl SSL_HELPERS = ca-stamp TESTS = @TESTS@ -HDRS = common/tests.h common/child.h utils.h $(top_builddir)/config.h VALGRIND = valgrind --gdb-attach=yes --leak-check=yes -LIBTEST = libtest.a +# Make every object depend on libneon.la to force a rebuild on any src/* changes +OBJDEPS = $(srcdir)/common/tests.h $(srcdir)/common/child.h $(srcdir)/utils.h \ + $(top_builddir)/config.h $(top_builddir)/src/libneon.la +# Test program just depends on libtest +DEPS = $(LIBTEST) + +LIBTEST = libtest.la + +LIBTOOL = @LIBTOOL@ --silent +LINK = $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -no-install +COMPILE = $(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) + +.SUFFIXES: .lo .c # By default, compile but don't run the tests. all: $(TESTS) clean: - rm -f $(TESTS) $(HELPERS) *.o common/*.o libtest.a *.log - rm -rf ca + rm -f $(TESTS) $(HELPERS) *.o *.lo common/*.o libtest.a *.log + rm -rf ca .libs rm -f ca-stamp client.key *.csr ssigned.pem wrongcn.pem \ server.cert client.cert client.p12 @@ -47,15 +58,17 @@ check: $(TESTS) $(HELPERS) grind: $(TESTS) $(HELPERS) @SRCDIR=$(srcdir) HARNESS="$(VALGRIND)" $(SHELL) $(srcdir)/run.sh $(TESTS) -file1.gz: ../NEWS - gzip -c --no-name $(top_srcdir)/NEWS > $@ +NEWS = $(top_srcdir)/NEWS + +file1.gz: $(NEWS) + gzip -c --no-name $(NEWS) > $@ -file2.gz: ../NEWS - gzip -c --name $(top_srcdir)/NEWS > $@ +file2.gz: $(NEWS) + gzip -c --name $(NEWS) > $@ # gzip file with trailing bytes. -trailing.gz: ../NEWS - gzip -c --no-name $(top_srcdir)/NEWS > $@ +trailing.gz: $(NEWS) + gzip -c --no-name $(NEWS) > $@ echo "hello, world" >> $@ truncated.gz: file1.gz @@ -65,6 +78,13 @@ badcsum.gz: file1.gz dd of=$@ if=file1.gz bs=1 count=`perl -e 'printf "%d", (stat("file1.gz"))[7] - 8;'` echo 'broken!' >> $@ +corrupt1.gz: file1.gz + dd of=$@ if=file1.gz bs=1 count=500 + cat $(NEWS) >> $@ + +corrupt2.gz: + cat $(NEWS) > $@ + # Dummy target to create the CA keys etc. makekeys stderr is redirected # since it changes for every invocation; not helpful for regression # testing. @@ -77,95 +97,58 @@ ca-stamp: $(srcdir)/makekeys.sh $(srcdir)/openssl.conf Makefile: $(srcdir)/Makefile.in cd .. && ./config.status test/Makefile -LIBOBJS = common/tests.o common/child.o utils.o - -$(LIBTEST): $(LIBOBJS) $(HDRS) ../src/@NEON_TARGET@ - $(AR) cru $@ $(LIBOBJS) - $(RANLIB) $@ - -.c.o: - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ - -uri-tests: uri-tests.o $(LIBTEST) - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ uri-tests.o $(LIBS) - -request: request.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ request.o $(LIBS) - -string-tests: string-tests.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ string-tests.o $(LIBS) - -socket: socket.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ socket.o $(LIBS) - -compress: compress.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ compress.o $(LIBS) - -acl: acl.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ acl.o $(LIBS) - -utils: utils.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ utils.o $(LIBS) - -util-tests: util-tests.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ util-tests.o $(LIBS) +LIBOBJS = common/tests.lo common/child.lo utils.lo -auth: auth.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ auth.o $(LIBS) +$(LIBTEST): $(LIBOBJS) + $(LINK) -o $(LIBTEST) $(LIBOBJS) $(top_builddir)/src/libneon.la -lock: lock.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ lock.o $(LIBS) +.c.lo: + $(COMPILE) -c $< -o $@ -basic: basic.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ basic.o $(LIBS) +%:%.lo + $(LINK) -o $@ $< $(LIBS) # Recompile socket.c with SOCKET_SSL defined -socket-ssl.o: $(srcdir)/socket.c $(HDRS) - $(CC) -DSOCKET_SSL $(CPPFLAGS) $(CFLAGS) -c $(srcdir)/socket.c -o $@ - -socket-ssl: socket-ssl.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ socket-ssl.o $(LIBS) - -ssl: ssl.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ ssl.o $(LIBS) - -xml: xml.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ xml.o $(LIBS) - -stubs: stubs.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ stubs.o $(LIBS) - -redirect: redirect.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ redirect.o $(LIBS) - -session: session.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ session.o $(LIBS) - -cookies: cookies.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ cookies.o $(LIBS) - -props: props.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ props.o $(LIBS) - -resolve: resolve.o $(LIBTEST) - $(CC) $(LDFLAGS) -o $@ resolve.o $(LIBS) - -#FOO: FOO.o $(LIBTEST) -# $(CC) $(LDFLAGS) -o $@ FOO.o $(LIBS) - -auth.o: auth.c $(HDRS) -uri-tests.o: uri-tests.c $(HDRS) -util-tests.o: util-tests.c $(HDRS) -string-tests.o: string-tests.c $(HDRS) -socket.o: socket.c $(HDRS) -server.o: server.c $(HDRS) -request.o: request.c $(HDRS) -regress.o: regress.c $(HDRS) -compress.o: compress.c $(HDRS) -acl.o: acl.c $(HDRS) -utils.o: utils.c $(HDRS) -stubs.o: stubs.c $(HDRS) -props.o: props.c $(HDRS) -session.o: session.c $(HDRS) -redirect.o: redirect.c $(HDRS) -basic.o: basic.c $(HDRS) +socket-ssl.lo: $(srcdir)/socket.c $(HDRS) + $(COMPILE) -DSOCKET_SSL -c $(srcdir)/socket.c -o $@ + +socket-ssl: socket-ssl.lo $(LIBTEST) + $(LINK) -o $@ socket-ssl.lo $(LIBS) + +auth.lo: $(srcdir)/auth.c $(OBJDEPS) +uri-tests.lo: $(srcdir)/uri-tests.c $(OBJDEPS) +util-tests.lo: $(srcdir)/util-tests.c $(OBJDEPS) +string-tests.lo: $(srcdir)/string-tests.c $(OBJDEPS) +socket.lo: $(srcdir)/socket.c $(OBJDEPS) +server.lo: $(srcdir)/server.c $(OBJDEPS) +request.lo: $(srcdir)/request.c $(OBJDEPS) +regress.lo: $(srcdir)/regress.c $(OBJDEPS) +compress.lo: $(srcdir)/compress.c $(OBJDEPS) +acl.lo: $(srcdir)/acl.c $(OBJDEPS) +utils.lo: $(srcdir)/utils.c $(OBJDEPS) +stubs.lo: $(srcdir)/stubs.c $(OBJDEPS) +props.lo: $(srcdir)/props.c $(OBJDEPS) +session.lo: $(srcdir)/session.c $(OBJDEPS) +redirect.lo: $(srcdir)/redirect.c $(OBJDEPS) +basic.lo: $(srcdir)/basic.c $(OBJDEPS) +ssl.lo: $(srcdir)/ssl.c $(OBJDEPS) + +auth: auth.lo $(DEPS) +uri-tests: uri-tests.lo $(DEPS) +util-tests: util-tests.lo $(DEPS) +string-tests: string-tests.lo $(DEPS) +socket: socket.lo $(DEPS) +server: server.lo $(DEPS) +request: request.lo $(DEPS) +regress: regress.lo $(DEPS) +compress: compress.lo $(DEPS) +acl: acl.lo $(DEPS) +utils: utils.lo $(DEPS) +stubs: stubs.lo $(DEPS) +props: props.lo $(DEPS) +session: session.lo $(DEPS) +redirect: redirect.lo $(DEPS) +basic: basic.lo $(DEPS) +ssl: ssl.lo $(DEPS) +xml: xml.lo $(DEPS) +lock: lock.lo $(DEPS) diff --git a/test/common/.cvsignore b/test/common/.cvsignore new file mode 100644 index 0000000..c8b522d --- /dev/null +++ b/test/common/.cvsignore @@ -0,0 +1 @@ +*.lo diff --git a/test/common/ChangeLog b/test/common/ChangeLog index 11d9840..25d103f 100644 --- a/test/common/ChangeLog +++ b/test/common/ChangeLog @@ -1,3 +1,13 @@ +Fri Jul 25 12:13:59 2003 Joe Orton <joe@manyfish.co.uk> + + Add support for test type which is expected to fail + memory leak checks. + + * tests.h (T_XLEAKY, T_EXPECT_LEAKS): New defines. + + * test.c (main) [NEON_MEMLEAK]: If T_EXPECT_LEAKS is set, fail if + the test did not leak memory. + Wed Jun 18 20:10:45 2003 Joe Orton <joe@manyfish.co.uk> * child.c (server_child, spawn_server_repeat): Adapt for new diff --git a/test/common/tests.c b/test/common/tests.c index 2ff6b5f..83f0c52 100644 --- a/test/common/tests.c +++ b/test/common/tests.c @@ -219,8 +219,13 @@ int main(int argc, char *argv[]) ne_alloc_used > allocated) { t_context("memory leak of %" NE_FMT_SIZE_T " bytes", ne_alloc_used - allocated); + fprintf(debug, "Blocks leaked: "); ne_alloc_dump(debug); result = FAIL; + } else if (tests[n].flags & T_EXPECT_LEAKS && result == OK && + ne_alloc_used == allocated) { + t_context("expected memory leak not detected"); + result = FAIL; } #endif diff --git a/test/common/tests.h b/test/common/tests.h index 72e8275..2e4e31a 100644 --- a/test/common/tests.h +++ b/test/common/tests.h @@ -49,6 +49,7 @@ typedef struct { /* possible values for flags: */ #define T_CHECK_LEAKS (1) /* check for memory leaks */ #define T_EXPECT_FAIL (2) /* expect failure */ +#define T_EXPECT_LEAKS (4) /* expect memory leak failures */ /* array of tests to run: must be defined by each test suite. */ extern ne_test tests[]; @@ -60,6 +61,8 @@ extern ne_test tests[]; #define T_XFAIL(fn) { fn, #fn, T_EXPECT_FAIL | T_CHECK_LEAKS } /* define a test function which isn't checked for memory leaks. */ #define T_LEAKY(fn) { fn, #fn, 0 } +/* define a test function which is expected to fail memory leak checks */ +#define T_XLEAKY(fn) { fn, #fn, T_EXPECT_LEAKS } /* current test number */ extern int test_num; diff --git a/test/compress.c b/test/compress.c index 720ebe0..6bab4ce 100644 --- a/test/compress.c +++ b/test/compress.c @@ -122,9 +122,12 @@ static int do_fetch(const char *realfn, const char *gzipfn, CALL(await_server()); - ONN("inflated response compare", failed); - if (!expect_fail) + if (!expect_fail) { + ONN("inflated response compare", failed); + /* reader will have set body.len == 0 if the whole body + * has been compared. */ ONN("inflated response truncated", body.len != 0); + } return OK; } @@ -198,6 +201,16 @@ static int fail_bad_csum(void) return do_fetch(newsfn, "badcsum.gz", 0, 1); } +static int fail_corrupt1(void) +{ + return do_fetch(newsfn, "corrupt1.gz", 0, 1); +} + +static int fail_corrupt2(void) +{ + return do_fetch(newsfn, "corrupt2.gz", 0, 1); +} + ne_test tests[] = { T_LEAKY(init), T(not_compressed), @@ -206,6 +219,8 @@ ne_test tests[] = { T(fail_trailing), T(fail_bad_csum), T(fail_truncate), + T(fail_corrupt1), + T(fail_corrupt2), T(chunked_1b), T(chunked_1b_wn), T(chunked_12b), diff --git a/test/lock.c b/test/lock.c index 5fb80d4..0adf9e8 100644 --- a/test/lock.c +++ b/test/lock.c @@ -534,7 +534,7 @@ ne_test tests[] = { T(lock_timeout), T(lock_shared), T(discover), - T(fail_discover), + T_XLEAKY(fail_discover), T(NULL) }; diff --git a/test/run.sh b/test/run.sh index 7d72749..0464571 100644 --- a/test/run.sh +++ b/test/run.sh @@ -3,14 +3,11 @@ rm -f debug.log rm -f child.log -# for shared builds. -LD_LIBRARY_PATH=../src/.libs:$LD_LIBRARY_PATH - # enable an safety-checking malloc in glibc which will abort() if # heap corruption is detected. MALLOC_CHECK_=2 -export LD_LIBRARY_PATH MALLOC_CHECK_ +export MALLOC_CHECK_ for f in $*; do if ${HARNESS} ./$f ${SRCDIR}; then diff --git a/test/string-tests.c b/test/string-tests.c index 93c8508..2ed7c53 100644 --- a/test/string-tests.c +++ b/test/string-tests.c @@ -389,7 +389,7 @@ static int b64_check(const unsigned char *raw, size_t len, ONV(memcmp(raw, decoded, dlen), ("decoded `%s' as `%.*s' not `%.*s'", - expected, dlen, decoded, dlen, raw)); + expected, dlen, decoded, (int)dlen, raw)); ne_free(decoded); ne_free(encoded); |