From 815aa28e20edc460a309e6faee73e71cbd4736ba Mon Sep 17 00:00:00 2001 From: joe Date: Sat, 2 Oct 2004 19:29:59 +0000 Subject: 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 --- ChangeLog | 4 + Makefile.in | 2 +- NEWS | 7 + THANKS | 1 + macros/ChangeLog | 9 + macros/neon-test.m4 | 2 +- macros/neon.m4 | 14 +- src/ChangeLog | 32 ++++ src/ne_auth.c | 70 +++++--- src/ne_cookies.c | 4 +- src/ne_openssl.c | 16 +- src/ne_request.c | 28 +-- src/ne_xml.c | 1 + src/ne_xml.h | 30 ++-- test/.cvsignore | 1 + test/ChangeLog | 24 +++ test/Makefile.in | 2 + test/common/ChangeLog | 5 + test/common/child.c | 17 +- test/cookies.c | 4 + test/makekeys.sh | 21 +-- test/ssl.c | 474 +++++++++++++++++++++++++++++--------------------- test/string-tests.c | 2 +- test/uri-tests.c | 20 +-- test/xml.c | 19 +- 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 + + * Makefile.in: Respect @datadir@ from configure (Max Bowsher). + Sun Sep 14 11:13:36 2003 Joe Orton * 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 Lee Mallabone Magnus Sirwiö Markus Mueller +Max Bowsher Michael Sobolev Mike Rosellini Mo DeJong 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 + + * neon-test.m4 (NEON_TEST): Check for shutdown. + +Mon Sep 22 21:20:37 2003 Joe Orton + + * neon.m4 (NE_CHECK_FUNCS, NE_SEARCH_LIBS): Avoid clobbering + ne_save_{LIBS,CPPFLAGS}. + Sun Sep 14 10:51:34 2003 Joe Orton * 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 -*- autoconf -*- +# Copyright (C) 1998-2003 Joe Orton -*- 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 + + 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 + + * 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 + + * ne_auth.c [WIN32]: Include windows.h to fix non-SSL build. + +Thu Sep 25 20:05:18 2003 Joe Orton + + * ne_xml.c (ne_xml_create): Specify an initial error string. + +Sun Sep 21 23:00:10 2003 Joe Orton + + * ne_cookies.c (set_cookie_hdl): Strip whitespace around cookie + name and value. + Sun Sep 14 10:50:01 2003 Joe Orton * 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 /* for getpid() */ #endif +#ifdef WIN32 +#include /* for GetCurrentThreadId() etc */ +#endif + #ifdef NEON_SSL #include #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 + + * 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 + + * 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 + + * 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 * 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 + + * child.c (close_socket): New function. + (server_child, spawn_server_repeat): Use it. + Fri Jul 25 12:13:59 2003 Joe Orton 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 "\r\n" +#define PFX "\r\n" #define E(ns, n) "<{" ns "}" n ">" static const struct { const char *in, *out; @@ -330,6 +330,20 @@ static int fail_parse(void) PFX "", PFX "", PFX "", +#if 0 + /* 2-byte encoding of '.': */ + PFX "" "\x2F\xC0\xAE\x2E\x2F" "", + /* 3-byte encoding of '.': */ + PFX "" "\x2F\xE0\x80\xAE\x2E\x2F" "", + /* 4-byte encoding of '.': */ + PFX "" "\x2F\xF0\x80\x80\xAE\x2E\x2F" "", + /* 5-byte encoding of '.': */ + PFX "" "\x2F\xF8\x80\x80\x80\xAE\x2E\x2F" "", + /* 6-byte encoding of '.': */ + PFX "" "\x2F\xFC\x80\x80\x80\x80\xAE\x2E\x2F" "", + /* two-byte encoding of '<' must not be parsed as a '<': */ + PFX "\xC0\xBC" "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); -- cgit v1.2.1