From 72d63a715ab3bb85109696154deb9b90d0495852 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 12 Aug 2014 01:40:11 +0000 Subject: MFC r266728,266731,266735,266736,268135,268960,269833 Update apr 1.4.8 -> 1.5.1 Update apr-util 1.5.2 -> 1.5.3 Update serf 1.3.4 -> 1.3.7 Update svnlite 1.8.8 -> 1.8.10 Deal with svnlite.1 manpage. --- contrib/serf/CHANGES | 16 +++- contrib/serf/auth/auth_spnego.c | 3 +- contrib/serf/buckets/ssl_buckets.c | 183 +++++++++++++++++++++++++++++-------- contrib/serf/serf.h | 2 +- contrib/serf/ssltunnel.c | 9 +- 5 files changed, 168 insertions(+), 45 deletions(-) (limited to 'contrib/serf') diff --git a/contrib/serf/CHANGES b/contrib/serf/CHANGES index e4e1c89..6d39b0c 100644 --- a/contrib/serf/CHANGES +++ b/contrib/serf/CHANGES @@ -1,4 +1,18 @@ -Serf 1.3.4 [2014-02-08, from /tags/1.3.4, rxxxx] +Serf 1.3.7 [2014-08-11, from /tags/1.3.7, r2411] + Handle NUL bytes in fields of an X.509 certificate. (r2393, r2399) + +Serf 1.3.6 [2014-06-09, from /tags/1.3.6, r2372] + Revert r2319 from serf 1.3.5: this change was making serf call handle_response + multiple times in case of an error response, leading to unexpected behavior. + +Serf 1.3.5 [2014-04-27, from /tags/1.3.5, r2355] + Fix issue #125: no reverse lookup during Negotiate authentication for proxies. + Fix a crash caused by incorrect reuse of the ssltunnel CONNECT request (r2316) + Cancel request if response parsing failed + authn callback set (r2319) + Update the expired certificates in the test suite. + + +Serf 1.3.4 [2014-02-08, from /tags/1.3.4, r2310] Fix issue #119: Endless loop during ssl tunnel setup with Negotiate authn Fix issue #123: Can't setup ssl tunnel which sends Connection close header Fix a race condition when initializing OpenSSL from multiple threads (r2263) diff --git a/contrib/serf/auth/auth_spnego.c b/contrib/serf/auth/auth_spnego.c index c0ad27e..e55fad2 100644 --- a/contrib/serf/auth/auth_spnego.c +++ b/contrib/serf/auth/auth_spnego.c @@ -335,8 +335,7 @@ do_auth(peer_t peer, &tmp, &tmp_len, gss_info); } else { - char *proxy_host; - apr_getnameinfo(&proxy_host, conn->ctx->proxy_address, 0); + char *proxy_host = conn->ctx->proxy_address->hostname; status = gss_api_get_credentials(conn, token, token_len, proxy_host, &tmp, &tmp_len, diff --git a/contrib/serf/buckets/ssl_buckets.c b/contrib/serf/buckets/ssl_buckets.c index 1a27d3f..d2fe51d 100644 --- a/contrib/serf/buckets/ssl_buckets.c +++ b/contrib/serf/buckets/ssl_buckets.c @@ -202,6 +202,8 @@ struct serf_ssl_certificate_t { }; static void disable_compression(serf_ssl_context_t *ssl_ctx); +static char * + pstrdup_escape_nul_bytes(const char *buf, int len, apr_pool_t *pool); #if SSL_VERBOSE /* Log all ssl alerts that we receive from the server. */ @@ -427,6 +429,85 @@ static BIO_METHOD bio_file_method = { #endif }; +typedef enum san_copy_t { + EscapeNulAndCopy = 0, + ErrorOnNul = 1, +} san_copy_t; + + +static apr_status_t +get_subject_alt_names(apr_array_header_t **san_arr, X509 *ssl_cert, + san_copy_t copy_action, apr_pool_t *pool) +{ + STACK_OF(GENERAL_NAME) *names; + + /* assert: copy_action == ErrorOnNul || (san_arr && pool) */ + + if (san_arr) { + *san_arr = NULL; + } + + /* Get subjectAltNames */ + names = X509_get_ext_d2i(ssl_cert, NID_subject_alt_name, NULL, NULL); + if (names) { + int names_count = sk_GENERAL_NAME_num(names); + int name_idx; + + if (san_arr) + *san_arr = apr_array_make(pool, names_count, sizeof(char*)); + for (name_idx = 0; name_idx < names_count; name_idx++) { + char *p = NULL; + GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, name_idx); + + switch (nm->type) { + case GEN_DNS: + if (copy_action == ErrorOnNul && + strlen(nm->d.ia5->data) != nm->d.ia5->length) + return SERF_ERROR_SSL_CERT_FAILED; + if (san_arr && *san_arr) + p = pstrdup_escape_nul_bytes((const char *)nm->d.ia5->data, + nm->d.ia5->length, + pool); + break; + default: + /* Don't know what to do - skip. */ + break; + } + + if (p) { + APR_ARRAY_PUSH(*san_arr, char*) = p; + } + } + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + } + + return APR_SUCCESS; +} + +static apr_status_t validate_cert_hostname(X509 *server_cert, apr_pool_t *pool) +{ + char buf[1024]; + int length; + apr_status_t ret; + + ret = get_subject_alt_names(NULL, server_cert, ErrorOnNul, NULL); + if (ret) { + return ret; + } else { + /* Fail if the subject's CN field contains \0 characters. */ + X509_NAME *subject = X509_get_subject_name(server_cert); + if (!subject) + return SERF_ERROR_SSL_CERT_FAILED; + + length = X509_NAME_get_text_by_NID(subject, NID_commonName, buf, 1024); + if (length != -1) + if (strlen(buf) != length) + return SERF_ERROR_SSL_CERT_FAILED; + } + + return APR_SUCCESS; +} + static int validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) { @@ -435,6 +516,7 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) X509 *server_cert; int err, depth; int failures = 0; + apr_status_t status; ssl = X509_STORE_CTX_get_ex_data(store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); @@ -475,6 +557,11 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) } } + /* Validate hostname */ + status = validate_cert_hostname(server_cert, ctx->pool); + if (status) + failures |= SERF_SSL_CERT_UNKNOWN_FAILURE; + /* Check certificate expiry dates. */ if (X509_cmp_current_time(X509_get_notBefore(server_cert)) >= 0) { failures |= SERF_SSL_CERT_NOTYETVALID; @@ -485,7 +572,6 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) if (ctx->server_cert_callback && (depth == 0 || failures)) { - apr_status_t status; serf_ssl_certificate_t *cert; apr_pool_t *subpool; @@ -512,7 +598,6 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) if (ctx->server_cert_chain_callback && (depth == 0 || failures)) { - apr_status_t status; STACK_OF(X509) *chain; const serf_ssl_certificate_t **certs; int certs_len; @@ -1461,7 +1546,50 @@ serf_ssl_context_t *serf_bucket_ssl_encrypt_context_get( /* Functions to read a serf_ssl_certificate structure. */ -/* Creates a hash_table with keys (E, CN, OU, O, L, ST and C). */ +/* Takes a counted length string and escapes any NUL bytes so that + * it can be used as a C string. NUL bytes are escaped as 3 characters + * "\00" (that's a literal backslash). + * The returned string is allocated in POOL. + */ +static char * +pstrdup_escape_nul_bytes(const char *buf, int len, apr_pool_t *pool) +{ + int i, nul_count = 0; + char *ret; + + /* First determine if there are any nul bytes in the string. */ + for (i = 0; i < len; i++) { + if (buf[i] == '\0') + nul_count++; + } + + if (nul_count == 0) { + /* There aren't so easy case to just copy the string */ + ret = apr_pstrdup(pool, buf); + } else { + /* There are so we have to replace nul bytes with escape codes + * Proper length is the length of the original string, plus + * 2 times the number of nulls (for two digit hex code for + * the value) + the trailing null. */ + char *pos; + ret = pos = apr_palloc(pool, len + 2 * nul_count + 1); + for (i = 0; i < len; i++) { + if (buf[i] != '\0') { + *(pos++) = buf[i]; + } else { + *(pos++) = '\\'; + *(pos++) = '0'; + *(pos++) = '0'; + } + } + *pos = '\0'; + } + + return ret; +} + +/* Creates a hash_table with keys (E, CN, OU, O, L, ST and C). Any NUL bytes in + these fields in the certificate will be escaped as \00. */ static apr_hash_t * convert_X509_NAME_to_table(X509_NAME *org, apr_pool_t *pool) { @@ -1474,37 +1602,44 @@ convert_X509_NAME_to_table(X509_NAME *org, apr_pool_t *pool) NID_commonName, buf, 1024); if (ret != -1) - apr_hash_set(tgt, "CN", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + apr_hash_set(tgt, "CN", APR_HASH_KEY_STRING, + pstrdup_escape_nul_bytes(buf, ret, pool)); ret = X509_NAME_get_text_by_NID(org, NID_pkcs9_emailAddress, buf, 1024); if (ret != -1) - apr_hash_set(tgt, "E", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + apr_hash_set(tgt, "E", APR_HASH_KEY_STRING, + pstrdup_escape_nul_bytes(buf, ret, pool)); ret = X509_NAME_get_text_by_NID(org, NID_organizationalUnitName, buf, 1024); if (ret != -1) - apr_hash_set(tgt, "OU", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + apr_hash_set(tgt, "OU", APR_HASH_KEY_STRING, + pstrdup_escape_nul_bytes(buf, ret, pool)); ret = X509_NAME_get_text_by_NID(org, NID_organizationName, buf, 1024); if (ret != -1) - apr_hash_set(tgt, "O", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + apr_hash_set(tgt, "O", APR_HASH_KEY_STRING, + pstrdup_escape_nul_bytes(buf, ret, pool)); ret = X509_NAME_get_text_by_NID(org, NID_localityName, buf, 1024); if (ret != -1) - apr_hash_set(tgt, "L", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + apr_hash_set(tgt, "L", APR_HASH_KEY_STRING, + pstrdup_escape_nul_bytes(buf, ret, pool)); ret = X509_NAME_get_text_by_NID(org, NID_stateOrProvinceName, buf, 1024); if (ret != -1) - apr_hash_set(tgt, "ST", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + apr_hash_set(tgt, "ST", APR_HASH_KEY_STRING, + pstrdup_escape_nul_bytes(buf, ret, pool)); ret = X509_NAME_get_text_by_NID(org, NID_countryName, buf, 1024); if (ret != -1) - apr_hash_set(tgt, "C", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + apr_hash_set(tgt, "C", APR_HASH_KEY_STRING, + pstrdup_escape_nul_bytes(buf, ret, pool)); return tgt; } @@ -1550,7 +1685,7 @@ apr_hash_t *serf_ssl_cert_certificate( unsigned int md_size, i; unsigned char md[EVP_MAX_MD_SIZE]; BIO *bio; - STACK_OF(GENERAL_NAME) *names; + apr_array_header_t *san_arr; /* sha1 fingerprint */ if (X509_digest(cert->ssl_cert, EVP_sha1(), md, &md_size)) { @@ -1595,32 +1730,8 @@ apr_hash_t *serf_ssl_cert_certificate( BIO_free(bio); /* Get subjectAltNames */ - names = X509_get_ext_d2i(cert->ssl_cert, NID_subject_alt_name, NULL, NULL); - if (names) { - int names_count = sk_GENERAL_NAME_num(names); - - apr_array_header_t *san_arr = apr_array_make(pool, names_count, - sizeof(char*)); + if (!get_subject_alt_names(&san_arr, cert->ssl_cert, EscapeNulAndCopy, pool)) apr_hash_set(tgt, "subjectAltName", APR_HASH_KEY_STRING, san_arr); - for (i = 0; i < names_count; i++) { - char *p = NULL; - GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, i); - - switch (nm->type) { - case GEN_DNS: - p = apr_pstrmemdup(pool, (const char *)nm->d.ia5->data, - nm->d.ia5->length); - break; - default: - /* Don't know what to do - skip. */ - break; - } - if (p) { - APR_ARRAY_PUSH(san_arr, char*) = p; - } - } - sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); - } return tgt; } diff --git a/contrib/serf/serf.h b/contrib/serf/serf.h index c28703f..f6f34a7 100644 --- a/contrib/serf/serf.h +++ b/contrib/serf/serf.h @@ -1062,7 +1062,7 @@ void serf_debug__bucket_alloc_check( /* Version info */ #define SERF_MAJOR_VERSION 1 #define SERF_MINOR_VERSION 3 -#define SERF_PATCH_VERSION 4 +#define SERF_PATCH_VERSION 7 /* Version number string */ #define SERF_VERSION_STRING APR_STRINGIFY(SERF_MAJOR_VERSION) "." \ diff --git a/contrib/serf/ssltunnel.c b/contrib/serf/ssltunnel.c index 28ca127..0ad04d6 100644 --- a/contrib/serf/ssltunnel.c +++ b/contrib/serf/ssltunnel.c @@ -70,12 +70,11 @@ static apr_status_t handle_response(serf_request_t *request, req_ctx_t *ctx = handler_baton; serf_connection_t *conn = request->conn; - if (! response) { - serf_connection_request_create(conn, - setup_request, - ctx); + /* CONNECT request was cancelled. Assuming that this is during connection + reset, we can safely discard the request as a new one will be created + when setting up the next connection. */ + if (!response) return APR_SUCCESS; - } status = serf_bucket_response_status(response, &sl); if (SERF_BUCKET_READ_ERROR(status)) { -- cgit v1.1