diff -r b27f0c82a840 src/ne_openssl.c --- a/src/ne_openssl.c Wed Sep 02 13:25:47 2009 -0300 +++ b/src/ne_openssl.c Wed Sep 02 14:13:06 2009 -0300 @@ -215,23 +215,20 @@ void ne_ssl_cert_validity_time(const ne_ /* Return non-zero if hostname from certificate (cn) matches hostname * used for session (hostname). (Wildcard matching is no longer * mandated by RFC3280, but certs are deployed which use wildcards) */ -static int match_hostname(char *cn, const char *hostname) +static int match_hostname(const char *cn, size_t cnlen, const char *hostname) { const char *dot; - NE_DEBUG(NE_DBG_SSL, "Match %s on %s...\n", cn, hostname); - dot = strchr(hostname, '.'); - if (dot == NULL) { - char *pnt = strchr(cn, '.'); - /* hostname is not fully-qualified; unqualify the cn. */ - if (pnt != NULL) { - *pnt = '\0'; - } + + NE_DEBUG(NE_DBG_SSL, "ssl: Match common name '%s' against '%s'\n", + cn, hostname); + + if (strncmp(cn, "*.", 2) == 0 && cnlen > 2 + && (dot = strchr(hostname, '.')) != NULL) { + hostname = dot + 1; + cn += 2; + cnlen -= 2; } - else if (strncmp(cn, "*.", 2) == 0) { - hostname = dot + 1; - cn += 2; - } - return !ne_strcasecmp(cn, hostname); + return cnlen == strlen(hostname) && !ne_strcasecmp(cn, hostname); } /* Check certificate identity. Returns zero if identity matches; 1 if @@ -255,7 +252,7 @@ static int check_identity(const char *ho if (nm->type == GEN_DNS) { char *name = dup_ia5string(nm->d.ia5); if (identity && !found) *identity = ne_strdup(name); - match = match_hostname(name, hostname); + match = match_hostname(name, nm->d.ia5->length, hostname); ne_free(name); found = 1; } else if (nm->type == GEN_IPADD) { @@ -314,7 +311,7 @@ static int check_identity(const char *ho return -1; } if (identity) *identity = ne_strdup(cname->data); - match = match_hostname(cname->data, hostname); + match = match_hostname(cname->data, cname->length, hostname); ne_buffer_destroy(cname); } diff -r b27f0c82a840 src/ne_string.c --- a/src/ne_string.c Wed Sep 02 13:25:47 2009 -0300 +++ b/src/ne_string.c Wed Sep 02 14:13:06 2009 -0300 @@ -36,10 +36,10 @@ #include <stdio.h> -#include <ctype.h> /* for isprint() etc in ne_strclean() */ - #include "ne_alloc.h" #include "ne_string.h" +/* hack for 0.28.x backport of ne_strnqdup, ne_buffer_qappend */ +#include "ne_private.h" char *ne_token(char **str, char separator) { @@ -239,6 +239,98 @@ void ne_buffer_altered(ne_buffer *buf) buf->used = strlen(buf->data) + 1; } +/* ascii_quote[n] gives the number of bytes needed by + * ne_buffer_qappend() to append character 'n'. */ +static const unsigned char ascii_quote[256] = { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +static const char hex_chars[16] = "0123456789ABCDEF"; + +/* Return the expected number of bytes needed to append the string + * beginning at byte 's', where 'send' points to the last byte after + * 's'. */ +static size_t qappend_count(const unsigned char *s, const unsigned char *send) +{ + const unsigned char *p; + size_t ret; + + for (p = s, ret = 0; p < send; p++) { + ret += ascii_quote[*p]; + } + + return ret; +} + +/* Append the string 's', up to but not including 'send', to string + * 'dest', quoting along the way. Returns pointer to NUL. */ +static char *quoted_append(char *dest, const unsigned char *s, + const unsigned char *send) +{ + const unsigned char *p; + char *q = dest; + + for (p = s; p < send; p++) { + if (ascii_quote[*p] == 1) { + *q++ = *p; + } + else { + *q++ = '\\'; + *q++ = 'x'; + *q++ = hex_chars[(*p >> 4) & 0x0f]; + *q++ = hex_chars[*p & 0x0f]; + } + } + + /* NUL terminate after the last character */ + *q = '\0'; + + return q; +} + +void ne__buffer_qappend(ne_buffer *buf, const unsigned char *data, size_t len) +{ + const unsigned char *dend = data + len; + char *q, *qs; + + ne_buffer_grow(buf, buf->used + qappend_count(data, dend)); + + /* buf->used >= 1, so this is safe. */ + qs = buf->data + buf->used - 1; + + q = quoted_append(qs, data, dend); + + /* used already accounts for a NUL, so increment by number of + * characters appended, *before* the NUL. */ + buf->used += q - qs; +} + +char *ne__strnqdup(const unsigned char *data, size_t len) +{ + const unsigned char *dend = data + len; + char *dest = malloc(qappend_count(data, dend) + 1); + + quoted_append(dest, data, dend); + + return dest; +} + + static const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" @@ -332,11 +424,50 @@ size_t ne_unbase64(const char *data, uns return outp - *out; } +/* Character map array; ascii_clean[n] = isprint(n) ? n : 0x20. Used + * by ne_strclean as a locale-independent isprint(). */ +static const unsigned char ascii_clean[256] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 +}; + char *ne_strclean(char *str) { - char *pnt; - for (pnt = str; *pnt; pnt++) - if (iscntrl(*pnt) || !isprint(*pnt)) *pnt = ' '; + unsigned char *pnt; + + for (pnt = (unsigned char *)str; *pnt; pnt++) + *pnt = (char)ascii_clean[*pnt]; + return str; } diff -r b27f0c82a840 src/ne_xml.c --- a/src/ne_xml.c Wed Sep 02 13:25:47 2009 -0300 +++ b/src/ne_xml.c Wed Sep 02 14:13:06 2009 -0300 @@ -405,6 +405,28 @@ static void end_element(void *userdata, destroy_element(elm); } +#if defined(HAVE_EXPAT) && XML_MAJOR_VERSION > 1 +/* Stop the parser if an entity declaration is hit. */ +static void entity_declaration(void *userData, const XML_Char *entityName, + int is_parameter_entity, const XML_Char *value, + int value_length, const XML_Char *base, + const XML_Char *systemId, const XML_Char *publicId, + const XML_Char *notationName) +{ + ne_xml_parser *parser = userData; + + NE_DEBUG(NE_DBG_XMLPARSE, "XML: entity declaration [%s]. Failing.\n", + entityName); + + XML_StopParser(parser->parser, XML_FALSE); +} +#elif defined(HAVE_EXPAT) +/* A noop default_handler. */ +static void default_handler(void *userData, const XML_Char *s, int len) +{ +} +#endif + /* Find a namespace definition for 'prefix' in given element, where * length of prefix is 'pfxlen'. Returns the URI or NULL. */ static const char *resolve_nspace(const struct element *elm, @@ -443,14 +465,34 @@ ne_xml_parser *ne_xml_create(void) XML_SetCharacterDataHandler(p->parser, char_data); XML_SetUserData(p->parser, (void *) p); XML_SetXmlDeclHandler(p->parser, decl_handler); + + /* Prevent the "billion laughs" attack against expat by disabling + * internal entity expansion. With 2.x, forcibly stop the parser + * if an entity is declared - this is safer and a more obvious + * failure mode. With older versions, installing a noop + * DefaultHandler means that internal entities will be expanded as + * the empty string, which is also sufficient to prevent the + * attack. */ +#if XML_MAJOR_VERSION > 1 + XML_SetEntityDeclHandler(p->parser, entity_declaration); #else + XML_SetDefaultHandler(p->parser, default_handler); +#endif + +#else /* HAVE_LIBXML */ p->parser = xmlCreatePushParserCtxt(&sax_handler, (void *)p, NULL, 0, NULL); if (p->parser == NULL) { abort(); } +#if LIBXML_VERSION < 20602 p->parser->replaceEntities = 1; +#else + /* Enable expansion of entities, and disable network access. */ + xmlCtxtUseOptions(p->parser, XML_PARSE_NOENT | XML_PARSE_NONET); #endif + +#endif /* HAVE_LIBXML || HAVE_EXPAT */ return p; }