summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2004-10-02 19:29:59 +0000
committerjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2004-10-02 19:29:59 +0000
commit815aa28e20edc460a309e6faee73e71cbd4736ba (patch)
treefdc505a71d237b01db6d60e4ccc161aa2b9f6b63
parent25b639dbf4f487992ad414d30101a70feade1029 (diff)
downloadneon-815aa28e20edc460a309e6faee73e71cbd4736ba.tar.gz
Import neon-0.24.3.
git-svn-id: http://svn.webdav.org/repos/projects/neon/branches/0.24.x@249 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845
-rw-r--r--ChangeLog4
-rw-r--r--Makefile.in2
-rw-r--r--NEWS7
-rw-r--r--THANKS1
-rw-r--r--macros/ChangeLog9
-rw-r--r--macros/neon-test.m42
-rw-r--r--macros/neon.m414
-rw-r--r--src/ChangeLog32
-rw-r--r--src/ne_auth.c70
-rw-r--r--src/ne_cookies.c4
-rw-r--r--src/ne_openssl.c16
-rw-r--r--src/ne_request.c28
-rw-r--r--src/ne_xml.c1
-rw-r--r--src/ne_xml.h30
-rw-r--r--test/.cvsignore1
-rw-r--r--test/ChangeLog24
-rw-r--r--test/Makefile.in2
-rw-r--r--test/common/ChangeLog5
-rw-r--r--test/common/child.c17
-rw-r--r--test/cookies.c4
-rwxr-xr-xtest/makekeys.sh21
-rw-r--r--test/ssl.c474
-rw-r--r--test/string-tests.c2
-rw-r--r--test/uri-tests.c20
-rw-r--r--test/xml.c19
25 files changed, 519 insertions, 290 deletions
diff --git a/ChangeLog b/ChangeLog
index fae93f1..895a094 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Mon Sep 22 20:56:21 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in: Respect @datadir@ from configure (Max Bowsher).
+
Sun Sep 14 11:13:36 2003 Joe Orton <joe@manyfish.co.uk>
* configure.in: Run NEON_TEST before LIBNEON_SOURCE_CHECKS, to
diff --git a/Makefile.in b/Makefile.in
index bd1833d..25b63eb 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -20,7 +20,7 @@ libdir = @libdir@
mandir = @mandir@
man1dir = $(mandir)/man1
man3dir = $(mandir)/man3
-datadir = $(prefix)/share
+datadir = @datadir@
docdir = $(datadir)/doc/neon-@NEON_VERSION@
includedir = @includedir@
neonincludes = $(includedir)/neon
diff --git a/NEWS b/NEWS
index ff2e78b..ec23abd 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+Changes in release 0.24.3:
+* Respect configure's --datadir argument (Max Bowsher).
+* Fix build on Windows when OpenSSL is not used.
+* Fix use of SSLv2 (spurious "Server did not present certificate" error).
+* When using SSL via a proxy, prevent leaking server auth credentials
+ to the proxy, or proxy auth credentials to the server.
+
Changes in release 0.24.2:
* Fix name resolver with some old versions of glibc.
* Fix problems with configure's "time_t format string" detection.
diff --git a/THANKS b/THANKS
index f600ae0..a586911 100644
--- a/THANKS
+++ b/THANKS
@@ -20,6 +20,7 @@ Keith Wannamaker <keith@wannamaker.org>
Lee Mallabone <lee0@callnetuk.com>
Magnus Sirwiö <sirwio@hotmail.com>
Markus Mueller <markus-m.mueller@ubs.com>
+Max Bowsher <maxb@ukf.net>
Michael Sobolev <mss@despair.spb.ru>
Mike Rosellini <m@icopyright.com>
Mo DeJong <mdejong@cygnus.com>
diff --git a/macros/ChangeLog b/macros/ChangeLog
index bcf9359..c728e31 100644
--- a/macros/ChangeLog
+++ b/macros/ChangeLog
@@ -1,3 +1,12 @@
+Tue Oct 7 21:20:16 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * neon-test.m4 (NEON_TEST): Check for shutdown.
+
+Mon Sep 22 21:20:37 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NE_CHECK_FUNCS, NE_SEARCH_LIBS): Avoid clobbering
+ ne_save_{LIBS,CPPFLAGS}.
+
Sun Sep 14 10:51:34 2003 Joe Orton <joe@manyfish.co.uk>
* neon.m4 (LIBNEON_SOURCE_CHECKS): Check for working AI_ADDRCONFIG
diff --git a/macros/neon-test.m4 b/macros/neon-test.m4
index 3e5f052..7a9fa23 100644
--- a/macros/neon-test.m4
+++ b/macros/neon-test.m4
@@ -31,7 +31,7 @@ AC_BEFORE([$0], [NEON_XML_PARSER])
AC_CHECK_HEADERS(sys/time.h)
-AC_CHECK_FUNCS(pipe isatty usleep)
+AC_CHECK_FUNCS(pipe isatty usleep shutdown)
AC_REQUIRE([NE_FIND_AR])
diff --git a/macros/neon.m4 b/macros/neon.m4
index 698487c..34d1eb6 100644
--- a/macros/neon.m4
+++ b/macros/neon.m4
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2002 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*-
+# Copyright (C) 1998-2003 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*-
#
# This file is free software; you may copy and/or distribute it with
# or without modifications, as long as this notice is preserved.
@@ -312,16 +312,16 @@ AC_DEFUN([NE_SEARCH_LIBS], [
AC_CACHE_CHECK([for library containing $1], [ne_cv_libsfor_$1], [
AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="none needed"], [
-ne_save_LIBS=$LIBS
+ne_sl_save_LIBS=$LIBS
ne_cv_libsfor_$1="not found"
for lib in $2; do
- LIBS="$ne_save_LIBS -l$lib $NEON_LIBS"
+ LIBS="$ne_sl_save_LIBS -l$lib $NEON_LIBS"
AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib"; break])
m4_if($3, [], [], dnl If $3 is specified, then...
- [LIBS="$ne_save_LIBS -l$lib $3 $NEON_LIBS"
+ [LIBS="$ne_sl_save_LIBS -l$lib $3 $NEON_LIBS"
AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib $3"; break])])
done
-LIBS=$ne_save_LIBS])])
+LIBS=$ne_sl_save_LIBS])])
if test "$ne_cv_libsfor_$1" = "not found"; then
m4_if($4, [], [AC_MSG_ERROR([could not find library containing $1])], [$4])
@@ -465,10 +465,10 @@ AC_DEFINE_UNQUOTED([NE_FMT_]translit($1, a-z, A-Z), "$ne_cv_fmt_$1",
dnl Wrapper for AC_CHECK_FUNCS; uses libraries from $NEON_LIBS.
AC_DEFUN([NE_CHECK_FUNCS], [
-ne_save_LIBS=$LIBS
+ne_cf_save_LIBS=$LIBS
LIBS="$LIBS $NEON_LIBS"
AC_CHECK_FUNCS($@)
-LIBS=$ne_save_LIBS])
+LIBS=$ne_cf_save_LIBS])
dnl Checks needed when compiling the neon source.
AC_DEFUN([LIBNEON_SOURCE_CHECKS], [
diff --git a/src/ChangeLog b/src/ChangeLog
index 36022c8..b6fc833 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,35 @@
+Tue Oct 7 20:52:06 2003 Joe Orton <joe@manyfish.co.uk>
+
+ When using SSL via a proxy, don't leak server auth credentials to
+ the proxy, and vice versa.
+
+ * ne_auth.c (auth_session): Add context field.
+ (ah_create): Ignore challenges in a bad context.
+ (ah_pre_send, ah_destroy): Check that the request-private cookie
+ is not NULL.
+ (auth_register): Take an isproxy flag; set context field
+ appropriately in session structure.
+ (ne_set_server_auth, ne_set_proxy_auth): Adjust accordingly.
+
+Tue Oct 7 19:58:52 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_openssl.c (ne_negotiate_ssl): If the returned cert chain was
+ NULL, try and create one from the peer certificate alone (fix for
+ use of SSLv2 connections).
+
+Mon Sep 29 21:57:40 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c [WIN32]: Include windows.h to fix non-SSL build.
+
+Thu Sep 25 20:05:18 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_xml.c (ne_xml_create): Specify an initial error string.
+
+Sun Sep 21 23:00:10 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_cookies.c (set_cookie_hdl): Strip whitespace around cookie
+ name and value.
+
Sun Sep 14 10:50:01 2003 Joe Orton <joe@manyfish.co.uk>
* ne_socket.c (ne_addr_resolve): Use result of autoconf test for
diff --git a/src/ne_auth.c b/src/ne_auth.c
index 966d0f4..c7f0267 100644
--- a/src/ne_auth.c
+++ b/src/ne_auth.c
@@ -46,6 +46,10 @@
#include <unistd.h> /* for getpid() */
#endif
+#ifdef WIN32
+#include <windows.h> /* for GetCurrentThreadId() etc */
+#endif
+
#ifdef NEON_SSL
#include <openssl/rand.h>
#endif
@@ -128,6 +132,13 @@ static const struct auth_class {
/* Authentication session state. */
typedef struct {
ne_session *sess;
+
+ /* Which context will auth challenges be accepted? */
+ enum {
+ AUTH_ANY, /* ignore nothing. */
+ AUTH_CONNECT, /* only in response to a CONNECT request. */
+ AUTH_NOTCONNECT /* only in non-CONNECT responsees */
+ } context;
/* Specifics for server/proxy auth. FIXME: need a better field
* name! */
@@ -976,25 +987,31 @@ static void ah_create(ne_request *req, void *session, const char *method,
const char *uri)
{
auth_session *sess = session;
- struct auth_request *areq = ne_calloc(sizeof *areq);
-
- NE_DEBUG(NE_DBG_HTTPAUTH, "ah_create, for %s\n", sess->spec->resp_hdr);
-
- areq->method = method;
- areq->uri = uri;
- areq->request = req;
-
- ne_add_response_header_handler(req, sess->spec->resp_hdr,
- ne_duplicate_header, &areq->auth_hdr);
-
+ int is_connect = strcmp(method, "CONNECT") == 0;
+
+ if (sess->context == AUTH_ANY ||
+ (is_connect && sess->context == AUTH_CONNECT) ||
+ (!is_connect && sess->context == AUTH_NOTCONNECT)) {
+ struct auth_request *areq = ne_calloc(sizeof *areq);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "ah_create, for %s\n", sess->spec->resp_hdr);
+
+ areq->method = method;
+ areq->uri = uri;
+ areq->request = req;
+
+ ne_add_response_header_handler(req, sess->spec->resp_hdr,
+ ne_duplicate_header, &areq->auth_hdr);
+
- ne_add_response_header_handler(req, sess->spec->resp_info_hdr,
- ne_duplicate_header,
- &areq->auth_info_hdr);
-
- sess->attempt = 0;
-
- ne_set_request_private(req, sess->spec->id, areq);
+ ne_add_response_header_handler(req, sess->spec->resp_info_hdr,
+ ne_duplicate_header,
+ &areq->auth_info_hdr);
+
+ sess->attempt = 0;
+
+ ne_set_request_private(req, sess->spec->id, areq);
+ }
}
@@ -1003,7 +1020,7 @@ static void ah_pre_send(ne_request *r, void *cookie, ne_buffer *request)
auth_session *sess = cookie;
struct auth_request *req = ne_get_request_private(r, sess->spec->id);
- if (!sess->can_handle) {
+ if (!sess->can_handle || !req) {
NE_DEBUG(NE_DBG_HTTPAUTH, "Not handling session.\n");
} else {
char *value;
@@ -1053,6 +1070,8 @@ static int ah_post_send(ne_request *req, void *cookie, const ne_status *status)
struct auth_request *areq = ne_get_request_private(req, sess->spec->id);
int ret = NE_OK;
+ if (!areq) return NE_OK;
+
NE_DEBUG(NE_DBG_HTTPAUTH,
"ah_post_send (#%d), code is %d (want %d), %s is %s\n",
sess->attempt, status->code, sess->spec->status_code,
@@ -1083,7 +1102,7 @@ static void ah_destroy(ne_request *req, void *session)
{
auth_session *sess = session;
struct auth_request *areq = ne_get_request_private(req, sess->spec->id);
- ne_free(areq);
+ if (areq) ne_free(areq);
}
static void free_auth(void *cookie)
@@ -1094,7 +1113,7 @@ static void free_auth(void *cookie)
ne_free(sess);
}
-static void auth_register(ne_session *sess,
+static void auth_register(ne_session *sess, int isproxy,
const struct auth_class *ahc, const char *id,
ne_auth_creds creds, void *userdata)
{
@@ -1105,6 +1124,11 @@ static void auth_register(ne_session *sess,
ahs->sess = sess;
ahs->spec = ahc;
+ if (strcmp(ne_get_scheme(sess), "https") == 0)
+ ahs->context = isproxy ? AUTH_CONNECT : AUTH_NOTCONNECT;
+ else
+ ahs->context = AUTH_ANY;
+
/* Register hooks */
ne_hook_create_request(sess, ah_create, ahs);
ne_hook_pre_send(sess, ah_pre_send, ahs);
@@ -1117,12 +1141,12 @@ static void auth_register(ne_session *sess,
void ne_set_server_auth(ne_session *sess, ne_auth_creds creds, void *userdata)
{
- auth_register(sess, &ah_server_class, HOOK_SERVER_ID, creds, userdata);
+ auth_register(sess, 0, &ah_server_class, HOOK_SERVER_ID, creds, userdata);
}
void ne_set_proxy_auth(ne_session *sess, ne_auth_creds creds, void *userdata)
{
- auth_register(sess, &ah_proxy_class, HOOK_PROXY_ID, creds, userdata);
+ auth_register(sess, 1, &ah_proxy_class, HOOK_PROXY_ID, creds, userdata);
}
void ne_forget_auth(ne_session *sess)
diff --git a/src/ne_cookies.c b/src/ne_cookies.c
index 1cc1a89..64b9311 100644
--- a/src/ne_cookies.c
+++ b/src/ne_cookies.c
@@ -70,7 +70,7 @@ static void set_cookie_hdl(void *userdata, const char *value)
NE_DEBUG(NE_DBG_HTTP, "New cookie.\n");
cook = ne_malloc(sizeof *cook);
memset(cook, 0, sizeof *cook);
- cook->name = ne_strdup(pairs[0]);
+ cook->name = ne_strdup(ne_shave(pairs[0], " \t\r\n"));
cook->next = cache->cookies;
cache->cookies = cook;
} else {
@@ -78,7 +78,7 @@ static void set_cookie_hdl(void *userdata, const char *value)
ne_free(cook->value);
}
- cook->value = ne_strdup(pairs[1]);
+ cook->value = ne_strdup(ne_shave(pairs[1], " \t\r\n"));
for (n = 2; pairs[n] != NULL; n+=2) {
if (!pairs[n+1]) continue;
diff --git a/src/ne_openssl.c b/src/ne_openssl.c
index d48dbb3..2fe89ec 100644
--- a/src/ne_openssl.c
+++ b/src/ne_openssl.c
@@ -486,6 +486,7 @@ int ne_negotiate_ssl(ne_request *req)
ne_ssl_context *ctx = sess->ssl_context;
ne_ssl_socket *sock;
STACK_OF(X509) *chain;
+ int freechain = 0; /* non-zero if chain should be free'd. */
NE_DEBUG(NE_DBG_SSL, "Doing SSL negotiation.\n");
@@ -507,13 +508,24 @@ int ne_negotiate_ssl(ne_request *req)
sock = ne_sock_sslsock(sess->socket);
chain = SSL_get_peer_cert_chain(sock->ssl);
+ /* For an SSLv2 connection, the cert chain will always be NULL. */
+ if (chain == NULL) {
+ X509 *cert = SSL_get_peer_certificate(sock->ssl);
+ if (cert) {
+ chain = sk_X509_new_null();
+ sk_X509_push(chain, cert);
+ }
+ }
+
if (chain == NULL || sk_X509_num(chain) == 0) {
ne_set_error(sess, _("SSL server did not present certificate"));
return NE_ERROR;
}
if (sess->server_cert) {
- if (X509_cmp(sk_X509_value(chain, 0), sess->server_cert->subject)) {
+ int diff = X509_cmp(sk_X509_value(chain, 0), sess->server_cert->subject);
+ if (freechain) sk_X509_free(chain); /* no longer need the chain */
+ if (diff) {
/* This could be a MITM attack: fail the request. */
ne_set_error(sess, _("Server certificate changed: "
"connection intercepted?"));
@@ -525,6 +537,8 @@ int ne_negotiate_ssl(ne_request *req)
/* new connection: create the chain. */
ne_ssl_certificate *cert = make_chain(chain);
+ if (freechain) sk_X509_free(chain); /* no longer need the chain */
+
if (check_certificate(sess, sock->ssl, cert)) {
NE_DEBUG(NE_DBG_SSL, "SSL certificate checks failed: %s\n",
sess->error);
diff --git a/src/ne_request.c b/src/ne_request.c
index 85287de..678dc4b 100644
--- a/src/ne_request.c
+++ b/src/ne_request.c
@@ -878,13 +878,17 @@ static inline void strip_eol(char *buf, ssize_t *len)
}
}
-/* For persistent connection handling: if the first socket operation
- * on an already-open connection fails with an EOF error, then presume
- * a persistent connection timeout has occurred, and the request
- * should be retried. The 'retry' flag is zero once a socket
- * operation has succeeded on the connection. RETRY_RET() crafts the
- * appropriate return value given a 'retry' flag, the socket error
- * 'code', and the return value 'acode' from the aborted() function. */
+/* For accurate persistent connection handling, for any write() or
+ * read() operation for a new request on an already-open connection,
+ * an EOF or RST error MUST be treated as a persistent connection
+ * timeout, and the request retried on a new connection. Once a
+ * read() operation has succeeded, any subsequent error MUST be
+ * treated as fatal. A 'retry' flag is used; retry=1 represents the
+ * first case, retry=0 the latter. */
+
+/* RETRY_RET() crafts a function return value given the 'retry' flag,
+ * the socket error 'code', and the return value 'acode' from the
+ * aborted() function. */
#define RETRY_RET(retry, code, acode) \
((((code) == NE_SOCK_CLOSED || (code) == NE_SOCK_RESET || \
(code) == NE_SOCK_TRUNC) && retry) ? NE_RETRY : (acode))
@@ -938,7 +942,8 @@ static int send_request(ne_request *req, const ne_buffer *request)
{
ne_session *sess = req->session;
ssize_t ret = NE_OK;
- int sentbody = 0, retry;
+ int sentbody = 0; /* zero until body has been sent. */
+ int retry; /* non-zero whilst the request should be retried */
ne_status *status = &req->status;
/* Send the Request-Line and headers */
@@ -956,9 +961,6 @@ static int send_request(ne_request *req, const ne_buffer *request)
return RETRY_RET(retry, ret, aret);
}
- /* FIXME: probably due to Nagle, the write above may or may not
- * have been delayed, so retry is left at 1 here. */
-
if (!req->use_expect100 && req->body_size > 0) {
/* Send request body, if not using 100-continue. */
ret = send_request_body(req);
@@ -968,14 +970,14 @@ static int send_request(ne_request *req, const ne_buffer *request)
}
}
- NE_DEBUG(NE_DBG_HTTP, "Request sent; retry is %d\n", retry);
+ NE_DEBUG(NE_DBG_HTTP, "Request sent; retry is %d.\n", retry);
/* Loop eating interim 1xx responses (RFC2616 says these MAY be
* sent by the server, even if 100-continue is not used). */
while ((ret = read_status_line(req, status, retry)) == NE_OK
&& status->klass == 1) {
NE_DEBUG(NE_DBG_HTTP, "Interim %d response.\n", status->code);
- retry = 0;
+ retry = 0; /* successful read() => never retry now. */
/* Discard headers with the interim response. */
if ((ret = discard_headers(req)) != NE_OK) break;
diff --git a/src/ne_xml.c b/src/ne_xml.c
index d55d59c..3417b3a 100644
--- a/src/ne_xml.c
+++ b/src/ne_xml.c
@@ -403,6 +403,7 @@ ne_xml_parser *ne_xml_create(void)
p->current = p->root = ne_calloc(sizeof *p->root);
p->root->default_ns = "";
p->root->state = 0;
+ strcpy(p->error, _("Unknown error"));
#ifdef HAVE_EXPAT
p->parser = XML_ParserCreate(NULL);
if (p->parser == NULL) {
diff --git a/src/ne_xml.h b/src/ne_xml.h
index f767652..58cde9a 100644
--- a/src/ne_xml.h
+++ b/src/ne_xml.h
@@ -33,11 +33,11 @@ BEGIN_NEON_DECLS
* (start-element, char-data, end-element). Each start-element event
* is passed to each handler in the stack in turn until one until one
* accepts the element. This handler then receives subsequent
- * char-data and end-element events.
+ * char-data and end-element events for the element.
*
- * For each new start-element, the search up the handler stack begins
- * with the handler for the parent element (for the root element, at
- * the base of the stack).
+ * For each new start-element event, the search up the handler stack
+ * begins with the handler for the parent element (for the root
+ * element, at the base of the stack).
*
* For each accepted element, a "state" integer is stored, which is
* passed to the corresponding char-data and end-element callbacks for
@@ -50,12 +50,15 @@ BEGIN_NEON_DECLS
#define NE_XML_DECLINE (0)
#define NE_XML_ABORT (-1)
-/* The startelm callback may return:
- * <0 => abort the parse (NE_XML_ABORT)
- * 0 => decline this element (NE_XML_DECLINE)
- * >0 => accept this element; value is state for this element.
+/* A start-element callback for element with given namespace/name.
+ * The callback may return:
+ * <0 => abort the parse (NE_XML_ABORT)
+ * 0 => decline this element (NE_XML_DECLINE)
+ * >0 => accept this element; value is state for this element.
+ *
* The 'parent' integer is the state returned by the handler of the
- * parent element. */
+ * parent element. The attributes array gives name/value pairs
+ * in atts[n] and atts[n+1] from n=0 up to atts[n]==NULL. */
typedef int ne_xml_startelm_cb(void *userdata, int parent,
const char *nspace, const char *name,
const char **atts);
@@ -102,9 +105,10 @@ void ne_xml_parse_v(void *userdata, const char *block, size_t len);
/* Return current parse line for errors */
int ne_xml_currentline(ne_xml_parser *p);
-/* Set error message for parser */
+/* Set error string for parser. */
void ne_xml_set_error(ne_xml_parser *p, const char *msg);
+/* Return the error string for the parser and never NULL. */
const char *ne_xml_get_error(ne_xml_parser *p);
/* From a start_element callback which was passed 'attrs' using given
@@ -115,11 +119,11 @@ const char *ne_xml_get_attr(ne_xml_parser *parser,
const char *name);
/* Return the encoding of the document being parsed. May return NULL
- * if no encoding is defined or if the XML declaration has not been
- * parsed. */
+ * if no encoding is defined or if the XML declaration has not yet
+ * been parsed. */
const char *ne_xml_doc_encoding(const ne_xml_parser *p);
-/* A utility interface for mapping {nspace, name} onto an int. */
+/* A utility interface for mapping {nspace, name} onto an integer. */
struct ne_xml_idmap {
const char *nspace, *name;
int id;
diff --git a/test/.cvsignore b/test/.cvsignore
index acc3a9a..2304dc8 100644
--- a/test/.cvsignore
+++ b/test/.cvsignore
@@ -33,6 +33,7 @@ core*
props
socket-ssl
resolve
+cookies
*.bb
*.da
*.bbg
diff --git a/test/ChangeLog b/test/ChangeLog
index 5f2fa4a..e7109db 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,27 @@
+Tue Oct 7 20:23:35 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ssl.c (ssl_server): Handle several requests if required.
+ (tunnel_header): New function.
+ (serve_tunnel): Fail with a 500 if the request included
+ an Authenticate header; take an ssl_server_args pointer
+ as userdata.
+ (fail_tunnel, proxy_tunnel): Adjust accordingly.
+ (apt_post_send): Adjust for 401 check.
+ (auth_tunnel_creds): New test.
+
+Wed Oct 1 00:30:25 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ssl.c (simple_sslv2): Regression test.
+ (ssl_server): Create SSL_CTX locally; optionally create an
+ SSLv2-only server.
+ (init_ssl): Do not create the SSL_CTX.
+
+Wed Sep 17 19:57:22 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ssl.c: Refactor around single SSL server function.
+ (ssl_server): Combination of do_ssl_response and old serve_*
+ functions. All callers updated.
+
Sun Sep 14 12:27:22 2003 Joe Orton <joe@manyfish.co.uk>
* socket.c (write_reset, read_reset): Skip if no RESET was
diff --git a/test/Makefile.in b/test/Makefile.in
index 23fe132..9d89e42 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -137,6 +137,7 @@ session.lo: $(srcdir)/session.c $(OBJDEPS)
redirect.lo: $(srcdir)/redirect.c $(OBJDEPS)
basic.lo: $(srcdir)/basic.c $(OBJDEPS)
ssl.lo: $(srcdir)/ssl.c $(OBJDEPS)
+cookies.lo: $(srcdir)/cookies.c $(OBJDEPS)
auth: auth.lo $(DEPS)
basic: basic.lo $(DEPS)
@@ -158,3 +159,4 @@ basic: basic.lo $(DEPS)
ssl: ssl.lo $(DEPS)
xml: xml.lo $(DEPS)
lock: lock.lo $(DEPS)
+cookies: cookies.lo $(DEPS)
diff --git a/test/common/ChangeLog b/test/common/ChangeLog
index 25d103f..7e89c08 100644
--- a/test/common/ChangeLog
+++ b/test/common/ChangeLog
@@ -1,3 +1,8 @@
+Tue Oct 7 21:19:56 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * child.c (close_socket): New function.
+ (server_child, spawn_server_repeat): Use it.
+
Fri Jul 25 12:13:59 2003 Joe Orton <joe@manyfish.co.uk>
Add support for test type which is expected to fail
diff --git a/test/common/child.c b/test/common/child.c
index 84f580e..5756882 100644
--- a/test/common/child.c
+++ b/test/common/child.c
@@ -131,6 +131,19 @@ void minisleep(void)
#endif
}
+/* close 'sock', performing lingering close to avoid premature RST. */
+static int close_socket(ne_socket *sock)
+{
+#ifdef HAVE_SHUTDOWN
+ char buf[20];
+ int fd = ne_sock_fd(sock);
+
+ shutdown(fd, 0);
+ while (ne_sock_read(sock, buf, sizeof buf) > 0);
+#endif
+ return ne_sock_close(sock);
+}
+
/* This runs as the child process. */
static int server_child(int readyfd, struct in_addr addr, int port,
server_fn callback, void *userdata)
@@ -153,7 +166,7 @@ static int server_child(int readyfd, struct in_addr addr, int port,
ret = callback(s, userdata);
- ne_sock_close(s);
+ close_socket(s);
return ret;
}
@@ -258,7 +271,7 @@ int spawn_server_repeat(int port, server_fn fn, void *userdata, int n)
NE_DEBUG(NE_DBG_HTTP, "child awaiting connection #%d.\n", count);
ONN("accept failed", ne_sock_accept(sock, listener));
ret = fn(sock, userdata);
- ne_sock_close(sock);
+ close_socket(sock);
NE_DEBUG(NE_DBG_HTTP, "child served request, %d.\n", ret);
if (ret) {
printf("server child failed: %s\n", test_context);
diff --git a/test/cookies.c b/test/cookies.c
index 706ca7e..abd1f36 100644
--- a/test/cookies.c
+++ b/test/cookies.c
@@ -75,9 +75,13 @@ static int parsing(void)
const char *hdr, *name, *value;
} cookies[] = {
{ "Set-Cookie: alpha=bar", "alpha", "bar" },
+#if 0
{ "Set-Cookie2: alpha=bar", "alpha", "bar" },
+#endif
{ "Set-Cookie: beta = bar", "beta", "bar" },
{ "Set-Cookie: delta = bar; norman=fish", "delta", "bar" },
+ /* parsing bug in <0.24.1 */
+ { "Set-Cookie: alpha=beta; path", "alpha", "beta" },
{ NULL, NULL, NULL }
};
int n;
diff --git a/test/makekeys.sh b/test/makekeys.sh
index bcf85b2..bb6bb04 100755
--- a/test/makekeys.sh
+++ b/test/makekeys.sh
@@ -134,20 +134,17 @@ for n in 1 2 3 4 5; do
-in altname${n}.csr -out altname${n}.cert
done
+MKPKCS12="${OPENSSL} pkcs12 -export -passout stdin -in client.cert -inkey client.key"
+
# generate a PKCS12 cert from the client cert: -passOUT because it's the
# passphrase on the OUTPUT cert, confusing...
-echo foobar | ${OPENSSL} pkcs12 -export -passout stdin \
- -name "Just A Neon Client Cert" \
- -in client.cert -inkey client.key -out client.p12
-
-# generate a PKCS12 cert with no password
-echo | ${OPENSSL} pkcs12 -export -passout stdin \
- -name "An Unencrypted Neon Client Cert" \
- -in client.cert -inkey client.key -out unclient.p12
-
-# generate a PKCS12 cert with no friendly name
-echo | ${OPENSSL} pkcs12 -export -passout stdin \
- -in client.cert -inkey client.key -out noclient.p12
+echo foobar | ${MKPKCS12} -name "Just A Neon Client Cert" -out client.p12
+
+# generate a PKCS#12 cert with no password and a friendly name
+echo | ${MKPKCS12} -name "An Unencrypted Neon Client Cert" -out unclient.p12
+
+# generate a PKCS#12 cert with no friendly name
+echo | ${MKPKCS12} -out noclient.p12
### a file containing a complete chain
diff --git a/test/ssl.c b/test/ssl.c
index fbdc630..248466a 100644
--- a/test/ssl.c
+++ b/test/ssl.c
@@ -57,9 +57,8 @@
"Cambridge, Cambridgeshire, GB"
#define CACERT_DNAME "Random Dept, Neosign, Oakland, California, US"
-static SSL_CTX *server_ctx = NULL;
-
static char *srcdir = ".";
+static char *server_key = NULL;
static ne_ssl_certificate *def_ca_cert = NULL, *def_server_cert;
static ne_ssl_client_cert *def_cli_cert;
@@ -77,44 +76,139 @@ static int s_strwrite(SSL *s, const char *buf)
return OK;
}
-/* Do an SSL response over socket given context; returning ssl session
- * structure in *sess if sess is non-NULL. */
-static int do_ssl_response(ne_socket *sock, SSL_CTX *ctx, SSL_SESSION **sess,
- const char *resp, int unclean)
+/* Arguments for running the SSL server */
+struct ssl_server_args {
+ char *cert; /* the server cert to present. */
+ const char *response; /* the response to send. */
+ int unclean; /* use an unclean shutdown if non-NULL */
+ int numreqs; /* number of request/responses to handle over the SSL connection. */
+
+ /* client cert handling: */
+ int require_cc; /* require a client cert if non-NULL */
+ const char *ca_list; /* file of CA certs to verify client cert against */
+ const char *send_ca; /* file of CA certs to send in client cert request */
+
+ /* session caching: */
+ int cache; /* use the session cache if non-zero */
+ SSL_SESSION *session; /* use to store copy of cached session. */
+ int count; /* internal use. */
+
+ int use_ssl2; /* force use of SSLv2 only */
+};
+
+/* default response string if args->response is NULL */
+#define DEF_RESP "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"
+
+/* An SSL server inna bun. */
+static int ssl_server(ne_socket *sock, void *userdata)
{
+ struct ssl_server_args *args = userdata;
int fd = ne_sock_fd(sock), ret;
/* we don't want OpenSSL to close this socket for us. */
BIO *bio = BIO_new_socket(fd, BIO_NOCLOSE);
char buf[BUFSIZ];
- SSL *ssl = SSL_new(ctx);
+ SSL *ssl;
+ static SSL_CTX *ctx = NULL;
+
+ if (ctx == NULL) {
+ ctx = SSL_CTX_new(args->use_ssl2 ? SSLv2_server_method()
+ : SSLv23_server_method());
+ }
+ ONV(ctx == NULL,
+ ("could not create SSL_CTX: %s", ERROR_SSL_STRING));
+ ONV(!SSL_CTX_use_PrivateKey_file(ctx, server_key, SSL_FILETYPE_PEM),
+ ("failed to load private key: %s", ERROR_SSL_STRING));
+
+ NE_DEBUG(NE_DBG_HTTP, "using server cert %s\n", args->cert);
+ ONN("failed to load certificate",
+ !SSL_CTX_use_certificate_file(ctx, args->cert, SSL_FILETYPE_PEM));
+
+ if (args->require_cc) {
+ /* require a client cert. */
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+
+ if (args->send_ca) {
+ /* the list of issuer DNs of these CAs is included in the
+ * certificate request message sent to the client */
+ SSL_CTX_set_client_CA_list(ctx,
+ SSL_load_client_CA_file(args->send_ca));
+ }
+
+ /* set default ca_list */
+ if (!args->ca_list) args->ca_list = CA_CERT;
+ }
+
+ if (args->ca_list) {
+ /* load the CA used to verify the client cert; also sent in the
+ * certificate exchange message. */
+ ONN("failed to load CA cert",
+ SSL_CTX_load_verify_locations(ctx, args->ca_list, NULL) != 1);
+ }
+
+ if (args->cache && args->count == 0) {
+ /* enable OpenSSL's internal session cache, enabling the
+ * negotiation to re-use a session if both sides support it. */
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+ }
+
+ ssl = SSL_new(ctx);
ONN("SSL_new failed", ssl == NULL);
+ args->count++;
+
SSL_set_bio(ssl, bio, bio);
ONV(SSL_accept(ssl) != 1,
("SSL_accept failed: %s", ERROR_SSL_STRING));
- ret = SSL_read(ssl, buf, BUFSIZ - 1);
- if (ret == 0)
- return 0; /* connection closed by parent; give up. */
- ONV(ret < 0, ("SSL_read failed (%d): %s", ret, ERROR_SSL_STRING));
+ /* loop handling requests: */
+ do {
+ const char *response = args->response ? args->response : DEF_RESP;
- buf[ret] = '\0';
-
- NE_DEBUG(NE_DBG_HTTP, "Request over SSL was: [%s]\n", buf);
+ ret = SSL_read(ssl, buf, BUFSIZ - 1);
+ if (ret == 0)
+ return 0; /* connection closed by parent; give up. */
+ ONV(ret < 0, ("SSL_read failed (%d): %s", ret, ERROR_SSL_STRING));
+
+ buf[ret] = '\0';
+
+ NE_DEBUG(NE_DBG_HTTP, "Request over SSL was: [%s]\n", buf);
+
+ if (strstr(buf, "Proxy-Authorization:") != NULL) {
+ NE_DEBUG(NE_DBG_HTTP, "Got Proxy-Auth header over SSL!\n");
+ response = "HTTP/1.1 500 Client Leaks Credentials\r\n"
+ "Content-Length: 0\r\n" "\r\n";
+ }
+
+ CALL(s_strwrite(ssl, response));
+
+ } while (--args->numreqs > 0);
- ONN("request over SSL contained Proxy-Authorization header",
- strstr(buf, "Proxy-Authorization:") != NULL);
+ /* copy out the session if requested. */
+ if (args->session) {
+ SSL_SESSION *sess = SSL_get1_session(ssl);
- CALL(s_strwrite(ssl, resp));
+#if NE_DEBUGGING
+ /* dump session to child.log for debugging. */
+ SSL_SESSION_print_fp(ne_debug_stream, sess);
+#endif
- /* copy out the session if requested. */
- if (sess) {
- *sess = SSL_get1_session(ssl);
+ if (args->count == 0) {
+ /* save the session */
+ args->session = sess;
+ } else {
+ /* could just to do this with SSL_CTX_sess_hits really,
+ * but this is a more thorough test. */
+ ONN("cached SSL session not used",
+ SSL_SESSION_cmp(args->session, sess));
+ SSL_SESSION_free(args->session);
+ SSL_SESSION_free(sess);
+ }
}
- if (!unclean) {
+ if (!args->unclean) {
/* Erk, shutdown is messy! See Eric Rescorla's article:
* http://www.linuxjournal.com/article.php?sid=4822 ; we'll just
* hide our heads in the sand here. */
@@ -125,81 +219,15 @@ static int do_ssl_response(ne_socket *sock, SSL_CTX *ctx, SSL_SESSION **sess,
return 0;
}
-#define DEF_RESP "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"
-
-/* Standard server callback to send an HTTP response; SSL negotiated
- * using certificate passed as userdata. */
-static int serve_ssl(ne_socket *sock, void *ud)
-{
- const char *cert = ud;
-
- NE_DEBUG(NE_DBG_HTTP, "using server cert %s\n", cert);
-
- ONN("failed to load certificate",
- !SSL_CTX_use_certificate_file(server_ctx, cert, SSL_FILETYPE_PEM));
-
- CALL(do_ssl_response(sock, server_ctx, NULL, DEF_RESP, 0));
-
- return OK;
-}
-
-static int serve_response_unclean(ne_socket *sock, void *ud)
-{
- const char *resp = ud;
-
- ONN("failed to load certificate",
- !SSL_CTX_use_certificate_file(server_ctx,
- SERVER_CERT, SSL_FILETYPE_PEM));
-
- CALL(do_ssl_response(sock, server_ctx, NULL, resp, 1));
-
- return OK;
-}
-
-/* Server function which requires the use of a client cert.
- * 'userdata' must be the name of the file giving acceptable CA
- * certificates. */
-static int serve_ccert(ne_socket *sock, void *ud)
-{
- const char *calist = ud;
-
- ONN("failed to load certificate",
- !SSL_CTX_use_certificate_file(server_ctx, SERVER_CERT, SSL_FILETYPE_PEM));
-
- /* require a client cert. */
- SSL_CTX_set_verify(server_ctx, SSL_VERIFY_PEER |
- SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
-
- /* load the CA used to verify the client cert. */
- ONN("failed to load CA cert",
- SSL_CTX_load_verify_locations(server_ctx, CA_CERT, NULL) != 1);
-
- if (calist) {
- /* send acceptable CA cert list to the client */
- SSL_CTX_set_client_CA_list(server_ctx, SSL_load_client_CA_file(calist));
- }
-
- CALL(do_ssl_response(sock, server_ctx, NULL, DEF_RESP, 0));
-
- return OK;
-}
-
/* serve_ssl wrapper which ignores server failure and always succeeds */
static int fail_serve(ne_socket *sock, void *ud)
{
- serve_ssl(sock, ud);
+ struct ssl_server_args args = {0};
+ args.cert = ud;
+ ssl_server(sock, &args);
return OK;
}
-/* Wrapper for serve_ssl which registers the verify location, so that
- * the CA cert will be sent along with the server cert itself in the
- * certificate exchange. */
-static int serve_ssl_chained(ne_socket *sock, void *ud)
-{
- SSL_CTX_load_verify_locations(server_ctx, "ca/cert.pem", NULL);
- return serve_ssl(sock, ud);
-}
-
#define DEFSESS (ne_session_create("https", "localhost", 7777))
/* Run a request in the given session. */
@@ -232,8 +260,6 @@ static int any_ssl_request(ne_session *sess, server_fn fn, void *server_ud,
static int init(void)
{
- char *server_key;
-
/* take srcdir as argv[1]. */
if (test_argc > 1) {
srcdir = test_argv[1];
@@ -247,16 +273,6 @@ static int init(void)
return FAILHARD;
}
- server_ctx = SSL_CTX_new(SSLv23_server_method());
- if (server_ctx == NULL) {
- t_context("could not create SSL_CTX: %s", ERROR_SSL_STRING);
- return FAILHARD;
- } else if (!SSL_CTX_use_PrivateKey_file(server_ctx, server_key,
- SSL_FILETYPE_PEM)) {
- t_context("failed to load private key: %s", ERROR_SSL_STRING);
- return FAILHARD;
- }
-
def_ca_cert = ne_ssl_cert_read(CA_CERT);
if (def_ca_cert == NULL) {
t_context("couldn't load CA cert %s", CA_CERT);
@@ -368,8 +384,9 @@ static int load_client_cert(void)
static int accept_signed_cert_for_hostname(char *cert, const char *hostname)
{
ne_session *sess = ne_session_create("https", hostname, 7777);
+ struct ssl_server_args args= {cert, 0};
/* no verify callback needed. */
- CALL(any_ssl_request(sess, serve_ssl, cert, CA_CERT, NULL, NULL));
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
ne_session_destroy(sess);
return OK;
}
@@ -385,24 +402,29 @@ static int simple(void)
return accept_signed_cert(SERVER_CERT);
}
+/* Test for SSL operation when server uses SSLv2 */
+static int simple_sslv2(void)
+{
+ ne_session *sess = ne_session_create("https", "localhost", 7777);
+ struct ssl_server_args args = {SERVER_CERT, 0};
+ args.use_ssl2 = 1;
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+ ne_session_destroy(sess);
+ return OK;
+}
+
/* Serves using HTTP/1.0 get-till-EOF semantics. */
static int serve_eof(ne_socket *sock, void *ud)
{
- const char *cert = ud;
-
- NE_DEBUG(NE_DBG_HTTP, "using server cert %s\n", cert);
-
- ONN("failed to load certificate",
- !SSL_CTX_use_certificate_file(server_ctx, cert, SSL_FILETYPE_PEM));
+ struct ssl_server_args args = {0};
- CALL(do_ssl_response(sock, server_ctx, NULL,
- "HTTP/1.0 200 OK\r\n"
- "Connection: close\r\n"
- "\r\n"
- "This is a response body, like it or not.",
- 0));
+ args.cert = ud;
+ args.response = "HTTP/1.0 200 OK\r\n"
+ "Connection: close\r\n"
+ "\r\n"
+ "This is a response body, like it or not.";
- return OK;
+ return ssl_server(sock, &args);
}
/* Test read-til-EOF behaviour with SSL. */
@@ -418,10 +440,12 @@ static int simple_eof(void)
static int empty_truncated_eof(void)
{
ne_session *sess = DEFSESS;
+ struct ssl_server_args args = {0};
+
+ args.cert = SERVER_CERT;
+ args.response = "HTTP/1.0 200 OK\r\n" "\r\n";
- CALL(any_ssl_request(sess, serve_response_unclean,
- "HTTP/1.0 200 OK\r\n" "\r\n",
- CA_CERT, NULL, NULL));
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
ne_session_destroy(sess);
return OK;
@@ -431,12 +455,16 @@ static int fail_truncated_eof(void)
{
ne_session *sess = DEFSESS;
int ret;
-
+ struct ssl_server_args args = {0};
+
+ args.cert = SERVER_CERT;
+ args.response = "HTTP/1.0 200 OK\r\n" "\r\n"
+ "This is some content\n"
+ "Followed by a truncation attack!\n";
+ args.unclean = 1;
+
ne_ssl_trust_cert(sess, def_ca_cert);
- CALL(spawn_server(7777, serve_response_unclean,
- "HTTP/1.0 200 OK\r\n" "\r\n"
- "This is some content\n"
- "Followed by a truncation attack!\n"));
+ CALL(spawn_server(7777, ssl_server, &args));
ret = any_request(sess, "/foo");
CALL(await_server());
@@ -490,13 +518,13 @@ static int wildcard_init(void)
static int wildcard_match(void)
{
ne_session *sess;
+ struct ssl_server_args args = {"wildcard.cert", 0};
PRECOND(wildcard_ok);
sess = ne_session_create("https", local_hostname, 7777);
- CALL(any_ssl_request(sess, serve_ssl,
- "wildcard.cert", CA_CERT, NULL, NULL));
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
ne_session_destroy(sess);
return OK;
@@ -599,10 +627,11 @@ static int parse_cert(void)
{
ne_session *sess = DEFSESS;
int ret = 0;
+ struct ssl_server_args args = {SERVER_CERT, 0};
/* don't give a CA cert; should force the verify callback to be
* used. */
- CALL(any_ssl_request(sess, serve_ssl, SERVER_CERT, NULL,
+ CALL(any_ssl_request(sess, ssl_server, &args, NULL,
check_cert, &ret));
ne_session_destroy(sess);
@@ -645,10 +674,13 @@ static int parse_chain(void)
{
ne_session *sess = DEFSESS;
int ret = 0;
+ struct ssl_server_args args = {SERVER_CERT, 0};
+
+ args.ca_list = "ca/cert.pem";
/* don't give a CA cert; should force the verify callback to be
* used. */
- CALL(any_ssl_request(sess, serve_ssl_chained, SERVER_CERT, NULL,
+ CALL(any_ssl_request(sess, ssl_server, &args, NULL,
check_chain, &ret));
ne_session_destroy(sess);
@@ -672,8 +704,9 @@ static int no_verify(void)
{
ne_session *sess = DEFSESS;
int count = 0;
+ struct ssl_server_args args = {SERVER_CERT, 0};
- CALL(any_ssl_request(sess, serve_ssl, SERVER_CERT, CA_CERT, count_vfy,
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, count_vfy,
&count));
ONN("verify callback called unnecessarily", count != 0);
@@ -687,12 +720,13 @@ static int cache_verify(void)
{
ne_session *sess = DEFSESS;
int ret, count = 0;
+ struct ssl_server_args args = {SERVER_CERT, 0};
/* force verify cert. */
- ret = any_ssl_request(sess, serve_ssl, SERVER_CERT, NULL, count_vfy,
+ ret = any_ssl_request(sess, ssl_server, &args, NULL, count_vfy,
&count);
- CALL(spawn_server(7777, serve_ssl, SERVER_CERT));
+ CALL(spawn_server(7777, ssl_server, &args));
ret = any_request(sess, "/foo2");
CALL(await_server());
@@ -805,65 +839,19 @@ static int fail_missing_CN(void)
return OK;
}
-struct scache_args {
- SSL_CTX *ctx;
- char *cert;
- int count;
- SSL_SESSION *sess;
-};
-
-/* FIXME: factor out shared code with serve_ssl */
-static int serve_scache(ne_socket *sock, void *ud)
-{
- struct scache_args *args = ud;
- SSL_SESSION *sess;
-
- if (args->count == 0) {
- /* enable OpenSSL's internal session cache, enabling the
- * negotiation to re-use a session if both sides support it. */
- SSL_CTX_set_session_cache_mode(args->ctx, SSL_SESS_CACHE_SERVER);
-
- ONN("failed to load certificate",
- !SSL_CTX_use_certificate_file(args->ctx,
- args->cert, SSL_FILETYPE_PEM));
- }
-
- args->count++;
-
- CALL(do_ssl_response(sock, args->ctx, &sess, DEF_RESP, 0));
-
- /* dump session to child.log for debugging. */
- SSL_SESSION_print_fp(ne_debug_stream, sess);
-
- if (args->count == 1) {
- /* save the session. */
- args->sess = sess;
- } else {
- /* could just to do this with SSL_CTX_sess_hits really,
- * but this is a more thorough test. */
- ONN("cached SSL session not used",
- SSL_SESSION_cmp(args->sess, sess));
- SSL_SESSION_free(args->sess);
- SSL_SESSION_free(sess);
- }
-
- return 0;
-}
-
/* Test that the SSL session is cached across connections. */
static int session_cache(void)
{
- struct scache_args args;
+ struct ssl_server_args args = {0};
ne_session *sess = ne_session_create("https", "localhost", 7777);
- args.ctx = server_ctx;
- args.count = 0;
args.cert = SERVER_CERT;
+ args.cache = 1;
ne_ssl_trust_cert(sess, def_ca_cert);
/* have spawned server listen for several connections. */
- CALL(spawn_server_repeat(7777, serve_scache, &args, 4));
+ CALL(spawn_server_repeat(7777, ssl_server, &args, 4));
ONREQ(any_request(sess, "/req1"));
ONREQ(any_request(sess, "/req2"));
@@ -891,6 +879,9 @@ static int client_cert_provided(void)
{
ne_session *sess = DEFSESS;
ne_ssl_client_cert *cc;
+ struct ssl_server_args args = {SERVER_CERT, NULL};
+
+ args.require_cc = 1;
cc = ne_ssl_clicert_read("client.p12");
ONN("could not load client.p12", cc == NULL);
@@ -898,7 +889,7 @@ static int client_cert_provided(void)
ne_ssl_clicert_decrypt(cc, "foobar"));
ne_ssl_provide_clicert(sess, ccert_provider, cc);
- CALL(any_ssl_request(sess, serve_ccert, NULL, CA_CERT,
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT,
NULL, NULL));
ne_session_destroy(sess);
@@ -944,12 +935,16 @@ static int cc_provided_dnames(void)
{
int check = 0;
ne_session *sess = DEFSESS;
+ struct ssl_server_args args = {SERVER_CERT, NULL};
+
+ args.require_cc = 1;
+ args.send_ca = "calist.pem";
PRECOND(def_cli_cert);
ne_ssl_provide_clicert(sess, cc_check_dnames, &check);
- CALL(any_ssl_request(sess, serve_ccert, "calist.pem", CA_CERT, NULL, NULL));
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
ne_session_destroy(sess);
@@ -962,11 +957,14 @@ static int cc_provided_dnames(void)
static int client_cert_pkcs12(void)
{
ne_session *sess = DEFSESS;
+ struct ssl_server_args args = {SERVER_CERT, NULL};
+
+ args.require_cc = 1;
PRECOND(def_cli_cert);
ne_ssl_set_clicert(sess, def_cli_cert);
- CALL(any_ssl_request(sess, serve_ccert, NULL, CA_CERT, NULL, NULL));
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
ne_session_destroy(sess);
return OK;
@@ -978,24 +976,51 @@ static int ccert_unencrypted(void)
{
ne_session *sess = DEFSESS;
ne_ssl_client_cert *ccert;
+ struct ssl_server_args args = {SERVER_CERT, NULL};
+
+ args.require_cc = 1;
ccert = ne_ssl_clicert_read("unclient.p12");
ONN("unclient.p12 was encrypted", ne_ssl_clicert_encrypted(ccert));
ne_ssl_set_clicert(sess, ccert);
- CALL(any_ssl_request(sess, serve_ccert, NULL, CA_CERT, NULL, NULL));
+ CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
ne_ssl_clicert_free(ccert);
ne_session_destroy(sess);
return OK;
}
+/* non-zero if a server auth header was received */
+static int got_server_auth;
+
+/* Utility function which accepts the 'tunnel' header. */
+static void tunnel_header(char *value)
+{
+ got_server_auth = 1;
+}
+
+/* Server which acts as a proxy accepting a CONNECT request. */
static int serve_tunnel(ne_socket *sock, void *ud)
{
+ struct ssl_server_args *args = ud;
+
+ /* check for a server auth function */
+ want_header = "Authorization";
+ got_header = tunnel_header;
+ got_server_auth = 0;
+
+ /* give the plaintext tunnel reply, acting as the proxy */
CALL(discard_request(sock));
-
- SEND_STRING(sock, "HTTP/1.1 200 OK\r\nServer: Fish\r\n\r\n");
- return serve_ssl(sock, ud);
+
+ if (got_server_auth) {
+ SEND_STRING(sock, "HTTP/1.1 500 Leaked Server Auth Creds\r\n"
+ "Content-Length: 0\r\n" "Server: serve_tunnel\r\n\r\n");
+ return 0;
+ } else {
+ SEND_STRING(sock, "HTTP/1.1 200 OK\r\nServer: serve_tunnel\r\n\r\n");
+ return ssl_server(sock, args);
+ }
}
/* neon versions <= 0.21.2 segfault here because ne_sock_close would
@@ -1005,9 +1030,10 @@ static int fail_tunnel(void)
{
ne_session *sess = ne_session_create("https", "example.com", 443);
ne_session_proxy(sess, "localhost", 7777);
+ struct ssl_server_args args = {SERVER_CERT, NULL};
ONN("server cert verification didn't fail",
- any_ssl_request(sess, serve_tunnel, SERVER_CERT, CA_CERT,
+ any_ssl_request(sess, serve_tunnel, &args, CA_CERT,
NULL, NULL) != NE_ERROR);
ne_session_destroy(sess);
@@ -1018,24 +1044,34 @@ static int proxy_tunnel(void)
{
ne_session *sess = ne_session_create("https", "localhost", 443);
ne_session_proxy(sess, "localhost", 7777);
+ struct ssl_server_args args = {SERVER_CERT, NULL};
/* CA cert is trusted, so no verify callback should be needed. */
- CALL(any_ssl_request(sess, serve_tunnel, SERVER_CERT, CA_CERT,
+ CALL(any_ssl_request(sess, serve_tunnel, &args, CA_CERT,
NULL, NULL));
ne_session_destroy(sess);
return OK;
}
+#define RESP_0LENGTH "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"
+
/* a tricky test which requires spawning a second server process in
* time for a new connection after a 407. */
static int apt_post_send(ne_request *req, void *ud, const ne_status *st)
{
- if (st->code == 407) {
- NE_DEBUG(NE_DBG_HTTP, "Got 407, awaiting server...\n");
+ int *code = ud;
+ if (st->code == *code) {
+ struct ssl_server_args args = {SERVER_CERT, NULL};
+
+ if (*code == 407) args.numreqs = 2;
+ args.response = RESP_0LENGTH;
+
+ NE_DEBUG(NE_DBG_HTTP, "Got challenge, awaiting server...\n");
CALL(await_server());
NE_DEBUG(NE_DBG_HTTP, "Spawning proper tunnel server...\n");
- CALL(spawn_server(7777, serve_tunnel, SERVER_CERT));
+ /* serve *two* 200 OK responses. */
+ CALL(spawn_server(7777, serve_tunnel, &args));
NE_DEBUG(NE_DBG_HTTP, "Spawned.\n");
}
return OK;
@@ -1046,7 +1082,7 @@ static int apt_creds(void *userdata, const char *realm, int attempt,
{
strcpy(username, "foo");
strcpy(password, "bar");
- return 0;
+ return attempt;
}
/* Test for using SSL over a CONNECT tunnel via a proxy server which
@@ -1055,29 +1091,55 @@ static int apt_creds(void *userdata, const char *realm, int attempt,
static int auth_proxy_tunnel(void)
{
ne_session *sess = ne_session_create("https", "localhost", 443);
- int ret;
+ int ret, code = 407;
ne_session_proxy(sess, "localhost", 7777);
- ne_hook_post_send(sess, apt_post_send, NULL);
+ ne_hook_post_send(sess, apt_post_send, &code);
ne_set_proxy_auth(sess, apt_creds, NULL);
+ ne_ssl_trust_cert(sess, def_ca_cert);
CALL(spawn_server(7777, single_serve_string,
"HTTP/1.0 407 I WANT MORE BISCUITS\r\n"
"Proxy-Authenticate: Basic realm=\"bigbluesea\"\r\n"
"Connection: close\r\n" "\r\n"));
- /* trust the CA */
- ne_ssl_trust_cert(sess, def_ca_cert);
- /* run the dreaded request. */
- ret = any_request(sess, "/foobar");
+ /* run two requests over the tunnel. */
+ ret = any_2xx_request(sess, "/foobar");
+ if (!ret) ret = any_2xx_request(sess, "/foobar2");
CALL(await_server());
- ONREQ(ret);
+ CALL(ret);
ne_session_destroy(sess);
return 0;
}
-/* Compare against known digest of notvalid.pem. Via:
+/* Regression test to check that server credentials aren't sent to the
+ * proxy in a CONNECT request. */
+static int auth_tunnel_creds(void)
+{
+ ne_session *sess = ne_session_create("https", "localhost", 443);
+ int ret, code = 401;
+ struct ssl_server_args args = {SERVER_CERT, 0};
+
+ ne_session_proxy(sess, "localhost", 7777);
+ ne_hook_post_send(sess, apt_post_send, &code);
+ ne_set_server_auth(sess, apt_creds, NULL);
+ ne_ssl_trust_cert(sess, def_ca_cert);
+
+ args.response = "HTTP/1.1 401 I want a Shrubbery\r\n"
+ "WWW-Authenticate: Basic realm=\"bigredocean\"\r\n"
+ "Server: Python\r\n" "Content-Length: 0\r\n" "\r\n";
+
+ CALL(spawn_server(7777, serve_tunnel, &args));
+ ret = any_2xx_request(sess, "/foobar");
+ CALL(await_server());
+ CALL(ret);
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+/* compare against known digest of notvalid.pem. Via:
* $ openssl x509 -fingerprint -sha1 -noout -in notvalid.pem */
#define THE_DIGEST "cf:5c:95:93:76:c6:3c:01:8b:62:" \
"b1:6f:f7:7f:42:32:ac:e6:69:1b"
@@ -1386,8 +1448,12 @@ static int cache_cert(void)
ne_session *sess = DEFSESS;
char *cache = NULL;
ne_ssl_certificate *cert;
+ struct ssl_server_args args = {0};
+
+ args.cert = "ssigned.pem";
+ args.cache = 1;
- ONREQ(any_ssl_request(sess, serve_ssl, "ssigned.pem", CA_CERT,
+ ONREQ(any_ssl_request(sess, ssl_server, &args, CA_CERT,
verify_cache, &cache));
ne_session_destroy(sess);
@@ -1404,7 +1470,7 @@ static int cache_cert(void)
ne_ssl_trust_cert(sess, cert);
ne_ssl_cert_free(cert);
/* now, the request should succeed without manual verification */
- ONREQ(any_ssl_request(sess, serve_ssl, "ssigned.pem", CA_CERT,
+ ONREQ(any_ssl_request(sess, ssl_server, &args, CA_CERT,
NULL, NULL));
ne_session_destroy(sess);
return OK;
@@ -1442,6 +1508,7 @@ ne_test tests[] = {
T(load_client_cert),
T(simple),
+ T(simple_sslv2),
T(simple_eof),
T(empty_truncated_eof),
T(fail_truncated_eof),
@@ -1483,6 +1550,7 @@ ne_test tests[] = {
T(fail_tunnel),
T(proxy_tunnel),
T(auth_proxy_tunnel),
+ T(auth_tunnel_creds),
T(NULL)
};
diff --git a/test/string-tests.c b/test/string-tests.c
index 2ed7c53..3d0fac1 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, (int)dlen, raw));
+ expected, (int)dlen, decoded, (int)dlen, raw));
ne_free(decoded);
ne_free(encoded);
diff --git a/test/uri-tests.c b/test/uri-tests.c
index ce35f22..753c376 100644
--- a/test/uri-tests.c
+++ b/test/uri-tests.c
@@ -288,16 +288,16 @@ static int parse(void)
for (n = 0; uritests[n].uri != NULL; n++) {
ne_uri res;
- const struct test_uri *exp = &uritests[n];
- ONV(ne_uri_parse(exp->uri, &res) != 0,
- ("%s: parse failed", exp->uri));
- ONV(res.port != exp->port,
- ("%s: parsed port was %d not %d", exp->uri, res.port, exp->port));
- ONCMP(exp->scheme, res.scheme, exp->uri, "scheme");
- ONCMP(exp->host, res.host, exp->uri, "host");
- ONV(strcmp(res.path, exp->path),
- ("%s: parsed path was %s not %s", exp->uri, res.path, exp->path));
- ONCMP(exp->authinfo, res.authinfo, exp->uri, "authinfo");
+ const struct test_uri *e = &uritests[n];
+ ONV(ne_uri_parse(e->uri, &res) != 0,
+ ("%s: parse failed", e->uri));
+ ONV(res.port != e->port,
+ ("%s: parsed port was %d not %d", e->uri, res.port, e->port));
+ ONCMP(e->scheme, res.scheme, e->uri, "scheme");
+ ONCMP(e->host, res.host, e->uri, "host");
+ ONV(strcmp(res.path, e->path),
+ ("%s: parsed path was %s not %s", e->uri, res.path, e->path));
+ ONCMP(e->authinfo, res.authinfo, e->uri, "authinfo");
ne_uri_free(&res);
}
diff --git a/test/xml.c b/test/xml.c
index 9d76215..9cea6c7 100644
--- a/test/xml.c
+++ b/test/xml.c
@@ -197,7 +197,7 @@ static int parse_match(const char *doc, const char *result, enum match_type t)
static int matches(void)
{
-#define PFX "<?xml version='1.0'?>\r\n"
+#define PFX "<?xml version='1.0' encoding='utf-8'?>\r\n"
#define E(ns, n) "<{" ns "}" n "></{" ns "}" n ">"
static const struct {
const char *in, *out;
@@ -330,6 +330,20 @@ static int fail_parse(void)
PFX "<foo xmlns:D=''/>",
PFX "<foo xmlns:='fish'/>",
PFX "<foo: xmlns:foo='bar'/>",
+#if 0
+ /* 2-byte encoding of '.': */
+ PFX "<foo>" "\x2F\xC0\xAE\x2E\x2F" "</foo>",
+ /* 3-byte encoding of '.': */
+ PFX "<foo>" "\x2F\xE0\x80\xAE\x2E\x2F" "</foo>",
+ /* 4-byte encoding of '.': */
+ PFX "<foo>" "\x2F\xF0\x80\x80\xAE\x2E\x2F" "</foo>",
+ /* 5-byte encoding of '.': */
+ PFX "<foo>" "\x2F\xF8\x80\x80\x80\xAE\x2E\x2F" "</foo>",
+ /* 6-byte encoding of '.': */
+ PFX "<foo>" "\x2F\xFC\x80\x80\x80\x80\xAE\x2E\x2F" "</foo>",
+ /* two-byte encoding of '<' must not be parsed as a '<': */
+ PFX "\xC0\xBC" "foo></foo>",
+#endif
NULL
};
int n;
@@ -424,6 +438,9 @@ static int errors(void)
ne_xml_parser *p = ne_xml_create();
const char *err;
+ ONV(strcmp(ne_xml_get_error(p), "Unknown error") != 0,
+ ("initial error string unspecified"));
+
ne_xml_set_error(p, "Fish food");
err = ne_xml_get_error(p);