xref: /aoo4110/ext_libraries/serf/NULbytes.patch (revision b1cdbd2c)
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