1------------------------------------------------------------------------ 2r1699925 | breser | 2014-08-04 11:04:00 -0700 (Mon, 04 Aug 2014) | 5 lines 3 4On the 1.3.x branch: 5 6Merge changes from trunk: 7- r2392: Handle NUL bytes in fields of X.509 certs. 8 9------------------------------------------------------------------------ 10Index: misc/build/serf-1.2.1/buckets/ssl_buckets.c 11=================================================================== 12--- misc/serf-1.2.1/buckets/ssl_buckets.c (revision 1699924) 13+++ misc/build/serf-1.2.1/buckets/ssl_buckets.c (revision 1699925) 14@@ -202,6 +202,8 @@ 15 }; 16 17 static void disable_compression(serf_ssl_context_t *ssl_ctx); 18+static char * 19+ pstrdup_escape_nul_bytes(const char *buf, int len, apr_pool_t *pool); 20 21 #if SSL_VERBOSE 22 /* Log all ssl alerts that we receive from the server. */ 23@@ -427,6 +429,81 @@ 24 #endif 25 }; 26 27+typedef enum san_copy_t { 28+ EscapeNulAndCopy = 0, 29+ ErrorOnNul = 1, 30+} san_copy_t; 31+ 32+ 33+static apr_status_t 34+get_subject_alt_names(apr_array_header_t **san_arr, X509 *ssl_cert, 35+ san_copy_t copy_action, apr_pool_t *pool) 36+{ 37+ STACK_OF(GENERAL_NAME) *names; 38+ 39+ /* assert: copy_action == ErrorOnNul || (san_arr && pool) */ 40+ 41+ /* Get subjectAltNames */ 42+ names = X509_get_ext_d2i(ssl_cert, NID_subject_alt_name, NULL, NULL); 43+ if (names) { 44+ int names_count = sk_GENERAL_NAME_num(names); 45+ int name_idx; 46+ 47+ if (san_arr) 48+ *san_arr = apr_array_make(pool, names_count, sizeof(char*)); 49+ for (name_idx = 0; name_idx < names_count; name_idx++) { 50+ char *p = NULL; 51+ GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, name_idx); 52+ 53+ switch (nm->type) { 54+ case GEN_DNS: 55+ if (copy_action == ErrorOnNul && 56+ strlen(nm->d.ia5->data) != nm->d.ia5->length) 57+ return SERF_ERROR_SSL_CERT_FAILED; 58+ if (san_arr && *san_arr) 59+ p = pstrdup_escape_nul_bytes((const char *)nm->d.ia5->data, 60+ nm->d.ia5->length, 61+ pool); 62+ break; 63+ default: 64+ /* Don't know what to do - skip. */ 65+ break; 66+ } 67+ 68+ if (p) { 69+ APR_ARRAY_PUSH(*san_arr, char*) = p; 70+ } 71+ } 72+ sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); 73+ } 74+ 75+ return APR_SUCCESS; 76+} 77+ 78+static apr_status_t validate_cert_hostname(X509 *server_cert, apr_pool_t *pool) 79+{ 80+ char buf[1024]; 81+ int length; 82+ apr_status_t ret; 83+ 84+ ret = get_subject_alt_names(NULL, server_cert, ErrorOnNul, NULL); 85+ if (ret) { 86+ return ret; 87+ } else { 88+ /* Fail if the subject's CN field contains \0 characters. */ 89+ X509_NAME *subject = X509_get_subject_name(server_cert); 90+ if (!subject) 91+ return SERF_ERROR_SSL_CERT_FAILED; 92+ 93+ length = X509_NAME_get_text_by_NID(subject, NID_commonName, buf, 1024); 94+ if (length != -1) 95+ if (strlen(buf) != length) 96+ return SERF_ERROR_SSL_CERT_FAILED; 97+ } 98+ 99+ return APR_SUCCESS; 100+} 101+ 102 static int 103 validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) 104 { 105@@ -435,6 +512,7 @@ 106 X509 *server_cert; 107 int err, depth; 108 int failures = 0; 109+ apr_status_t status; 110 111 ssl = X509_STORE_CTX_get_ex_data(store_ctx, 112 SSL_get_ex_data_X509_STORE_CTX_idx()); 113@@ -475,6 +553,11 @@ 114 } 115 } 116 117+ /* Validate hostname */ 118+ status = validate_cert_hostname(server_cert, ctx->pool); 119+ if (status) 120+ failures |= SERF_SSL_CERT_UNKNOWN_FAILURE; 121+ 122 /* Check certificate expiry dates. */ 123 if (X509_cmp_current_time(X509_get_notBefore(server_cert)) >= 0) { 124 failures |= SERF_SSL_CERT_NOTYETVALID; 125@@ -485,7 +568,6 @@ 126 127 if (ctx->server_cert_callback && 128 (depth == 0 || failures)) { 129- apr_status_t status; 130 serf_ssl_certificate_t *cert; 131 apr_pool_t *subpool; 132 133@@ -512,7 +594,6 @@ 134 135 if (ctx->server_cert_chain_callback 136 && (depth == 0 || failures)) { 137- apr_status_t status; 138 STACK_OF(X509) *chain; 139 const serf_ssl_certificate_t **certs; 140 int certs_len; 141@@ -1461,7 +1542,50 @@ 142 143 /* Functions to read a serf_ssl_certificate structure. */ 144 145-/* Creates a hash_table with keys (E, CN, OU, O, L, ST and C). */ 146+/* Takes a counted length string and escapes any NUL bytes so that 147+ * it can be used as a C string. NUL bytes are escaped as 3 characters 148+ * "\00" (that's a literal backslash). 149+ * The returned string is allocated in POOL. 150+ */ 151+static char * 152+pstrdup_escape_nul_bytes(const char *buf, int len, apr_pool_t *pool) 153+{ 154+ int i, nul_count = 0; 155+ char *ret; 156+ 157+ /* First determine if there are any nul bytes in the string. */ 158+ for (i = 0; i < len; i++) { 159+ if (buf[i] == '\0') 160+ nul_count++; 161+ } 162+ 163+ if (nul_count == 0) { 164+ /* There aren't so easy case to just copy the string */ 165+ ret = apr_pstrdup(pool, buf); 166+ } else { 167+ /* There are so we have to replace nul bytes with escape codes 168+ * Proper length is the length of the original string, plus 169+ * 2 times the number of nulls (for two digit hex code for 170+ * the value) + the trailing null. */ 171+ char *pos; 172+ ret = pos = apr_palloc(pool, len + 2 * nul_count + 1); 173+ for (i = 0; i < len; i++) { 174+ if (buf[i] != '\0') { 175+ *(pos++) = buf[i]; 176+ } else { 177+ *(pos++) = '\\'; 178+ *(pos++) = '0'; 179+ *(pos++) = '0'; 180+ } 181+ } 182+ *pos = '\0'; 183+ } 184+ 185+ return ret; 186+} 187+ 188+/* Creates a hash_table with keys (E, CN, OU, O, L, ST and C). Any NUL bytes in 189+ these fields in the certificate will be escaped as \00. */ 190 static apr_hash_t * 191 convert_X509_NAME_to_table(X509_NAME *org, apr_pool_t *pool) 192 { 193@@ -1474,37 +1598,44 @@ 194 NID_commonName, 195 buf, 1024); 196 if (ret != -1) 197- apr_hash_set(tgt, "CN", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); 198+ apr_hash_set(tgt, "CN", APR_HASH_KEY_STRING, 199+ pstrdup_escape_nul_bytes(buf, ret, pool)); 200 ret = X509_NAME_get_text_by_NID(org, 201 NID_pkcs9_emailAddress, 202 buf, 1024); 203 if (ret != -1) 204- apr_hash_set(tgt, "E", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); 205+ apr_hash_set(tgt, "E", APR_HASH_KEY_STRING, 206+ pstrdup_escape_nul_bytes(buf, ret, pool)); 207 ret = X509_NAME_get_text_by_NID(org, 208 NID_organizationalUnitName, 209 buf, 1024); 210 if (ret != -1) 211- apr_hash_set(tgt, "OU", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); 212+ apr_hash_set(tgt, "OU", APR_HASH_KEY_STRING, 213+ pstrdup_escape_nul_bytes(buf, ret, pool)); 214 ret = X509_NAME_get_text_by_NID(org, 215 NID_organizationName, 216 buf, 1024); 217 if (ret != -1) 218- apr_hash_set(tgt, "O", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); 219+ apr_hash_set(tgt, "O", APR_HASH_KEY_STRING, 220+ pstrdup_escape_nul_bytes(buf, ret, pool)); 221 ret = X509_NAME_get_text_by_NID(org, 222 NID_localityName, 223 buf, 1024); 224 if (ret != -1) 225- apr_hash_set(tgt, "L", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); 226+ apr_hash_set(tgt, "L", APR_HASH_KEY_STRING, 227+ pstrdup_escape_nul_bytes(buf, ret, pool)); 228 ret = X509_NAME_get_text_by_NID(org, 229 NID_stateOrProvinceName, 230 buf, 1024); 231 if (ret != -1) 232- apr_hash_set(tgt, "ST", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); 233+ apr_hash_set(tgt, "ST", APR_HASH_KEY_STRING, 234+ pstrdup_escape_nul_bytes(buf, ret, pool)); 235 ret = X509_NAME_get_text_by_NID(org, 236 NID_countryName, 237 buf, 1024); 238 if (ret != -1) 239- apr_hash_set(tgt, "C", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); 240+ apr_hash_set(tgt, "C", APR_HASH_KEY_STRING, 241+ pstrdup_escape_nul_bytes(buf, ret, pool)); 242 243 return tgt; 244 } 245@@ -1550,7 +1681,7 @@ 246 unsigned int md_size, i; 247 unsigned char md[EVP_MAX_MD_SIZE]; 248 BIO *bio; 249- STACK_OF(GENERAL_NAME) *names; 250+ apr_array_header_t *san_arr; 251 252 /* sha1 fingerprint */ 253 if (X509_digest(cert->ssl_cert, EVP_sha1(), md, &md_size)) { 254@@ -1595,33 +1726,9 @@ 255 BIO_free(bio); 256 257 /* Get subjectAltNames */ 258- names = X509_get_ext_d2i(cert->ssl_cert, NID_subject_alt_name, NULL, NULL); 259- if (names) { 260- int names_count = sk_GENERAL_NAME_num(names); 261- 262- apr_array_header_t *san_arr = apr_array_make(pool, names_count, 263- sizeof(char*)); 264+ if (!get_subject_alt_names(&san_arr, cert->ssl_cert, EscapeNulAndCopy, pool)) 265 apr_hash_set(tgt, "subjectAltName", APR_HASH_KEY_STRING, san_arr); 266- for (i = 0; i < names_count; i++) { 267- char *p = NULL; 268- GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, i); 269 270- switch (nm->type) { 271- case GEN_DNS: 272- p = apr_pstrmemdup(pool, (const char *)nm->d.ia5->data, 273- nm->d.ia5->length); 274- break; 275- default: 276- /* Don't know what to do - skip. */ 277- break; 278- } 279- if (p) { 280- APR_ARRAY_PUSH(san_arr, char*) = p; 281- } 282- } 283- sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); 284- } 285- 286 return tgt; 287 } 288 289------------------------------------------------------------------------ 290r1699931 | breser | 2014-08-05 19:24:00 -0700 (Tue, 05 Aug 2014) | 6 lines 291 292On the 1.3.x branch: 293 294Merge changes from trunk: 295- r2398: Initialize san_arr when we're expected to fill it. 296 297 298------------------------------------------------------------------------ 299Index: misc/build/serf-1.2.1/buckets/ssl_buckets.c 300=================================================================== 301--- misc/serf-1.2.1/buckets/ssl_buckets.c (revision 1699930) 302+++ misc/build/serf-1.2.1/buckets/ssl_buckets.c (revision 1699931) 303@@ -443,6 +443,10 @@ 304 305 /* assert: copy_action == ErrorOnNul || (san_arr && pool) */ 306 307+ if (san_arr) { 308+ *san_arr = NULL; 309+ } 310+ 311 /* Get subjectAltNames */ 312 names = X509_get_ext_d2i(ssl_cert, NID_subject_alt_name, NULL, NULL); 313 if (names) { 314