From 5a5727ff2889079c6957e90340e9b8af860589ef Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: May 05 2015 21:02:52 +0000 Subject: Handle 0 bytes in JSON strings Handle 0 bytes in JSON string data by explicitly passing around the lengths of strings. We still expect values to be UTF-8, so arbitrary binary data is still rejected. --- diff --git a/src/json.c b/src/json.c index bbf9cbd..13fd6f1 100644 --- a/src/json.c +++ b/src/json.c @@ -30,7 +30,10 @@ struct cm_json { enum cm_json_type type; union { - char *s; + struct { + char *s; + ssize_t l; + } s; long long l; long double d; unsigned char b; @@ -75,11 +78,17 @@ cm_json_new_string(void *parent, const char *string, ssize_t length) if (json != NULL) { json->type = cm_json_type_string; if (length < 0) { - json->s = talloc_strdup(json, string); + json->s.s = talloc_strdup(json, string); + json->s.l = strlen(json->s.s); } else { - json->s = talloc_strndup(json, string, length); + json->s.s = talloc_size(json, length + 1); + if (json->s.s != NULL) { + memcpy(json->s.s, string, length); + json->s.s[length] = '\0'; + } + json->s.l = length; } - if (json->s == NULL) { + if (json->s.s == NULL) { talloc_free(json); json = NULL; } @@ -155,12 +164,15 @@ cm_json_new_array(void *parent) } const char * -cm_json_string(struct cm_json *json) +cm_json_string(struct cm_json *json, ssize_t *length) { if (cm_json_type(json) != cm_json_type_string) { return NULL; } - return json->s; + if (length != NULL) { + *length = json->s.l; + } + return json->s.s; } long double @@ -368,7 +380,7 @@ cm_json_utf8_to_point(const char *p, uint32_t *point) } static char * -cm_json_escape(void *parent, const char *s) +cm_json_escape(void *parent, const char *s, ssize_t l) { char *ret, *q; const unsigned char *p; @@ -376,17 +388,20 @@ cm_json_escape(void *parent, const char *s) uint32_t uni; int esc = 0, n; - for (p = (const unsigned char *) s; *p != '\0'; p++) { + if (l < 0) { + l = strlen(s); + } + for (p = (const unsigned char *) s; (const char *) p < s + l; p++) { uc = *p; if ((uc < 0x20) || (uc == 0x22) || (uc == 0x5c) || (uc > 0x7f)) { esc++; } } - ret = talloc_size(parent, strlen(s) + esc * 12 + 2 + 1); + ret = talloc_size(parent, l + esc * 12 + 2 + 1); if (ret != NULL) { q = ret; *q++ = '"'; - for (p = (const unsigned char *) s; *p != '\0'; p++) { + for (p = (const unsigned char *) s; (const char *) p < s + l; p++) { uc = *p; switch (uc) { case '"': @@ -465,7 +480,7 @@ cm_json_encode(void *parent, struct cm_json *json) ret = talloc_strdup(parent, "null"); break; case cm_json_type_string: - ret = cm_json_escape(ret, json->s); + ret = cm_json_escape(ret, json->s.s, json->s.l); break; case cm_json_type_numberl: ret = talloc_asprintf(parent, "%lld", json->l); @@ -479,7 +494,7 @@ cm_json_encode(void *parent, struct cm_json *json) case cm_json_type_object: ret = talloc_strdup(parent, "{"); for (i = 0; i < json->o.n; i++) { - key = cm_json_escape(ret, json->o.o[i].key); + key = cm_json_escape(ret, json->o.o[i].key, -1); val = cm_json_encode(ret, json->o.o[i].val); if ((key == NULL) || (val == NULL)) { talloc_free(ret); @@ -562,13 +577,16 @@ cm_json_point_to_utf8(uint32_t point, char *out, ssize_t max) static char * cm_json_decode_string(void *parent, const char *s, ssize_t length, - const char **next) + const char **next, ssize_t *out_length) { char *ret = NULL, *q, *end; const char *p, *hex, *hexchars = "00112233445566778899AaBbCcDdEeFf", *psave; int unesc = 0, i; uint32_t point, point2; + if (out_length != NULL) { + *out_length = 0; + } *next = s; if (*s != '"') { return NULL; @@ -711,6 +729,9 @@ cm_json_decode_string(void *parent, const char *s, ssize_t length, } } *q = '\0'; + if (out_length != NULL) { + *out_length = q - ret; + } return ret; } @@ -733,6 +754,7 @@ cm_json_decode(void *parent, const char *encoded, ssize_t length, char *s = NULL, *tmp; struct cm_json *agg = NULL, *sub = NULL; enum cm_json_type aggtype; + ssize_t slength; enum {key, keyorclose, colon, commaorclose, expr, exprorclose} expect = expr; p = encoded; @@ -924,15 +946,15 @@ cm_json_decode(void *parent, const char *encoded, ssize_t length, if (s != NULL) { goto done; } - s = cm_json_decode_string(parent, p, length - (p - encoded), &p); + s = cm_json_decode_string(parent, p, length - (p - encoded), &p, &slength); if (s == NULL) { goto done; } - *json = cm_json_new_string(parent, s, -1); + *json = cm_json_new_string(parent, s, slength); talloc_free(s); s = NULL; } else { - tmp = cm_json_decode_string(parent, p, length - (p - encoded), &p); + tmp = cm_json_decode_string(parent, p, length - (p - encoded), &p, &slength); if (tmp == NULL) { goto done; } @@ -942,7 +964,7 @@ cm_json_decode(void *parent, const char *encoded, ssize_t length, expect = colon; } else { /* It's a value in an object or array. */ - sub = cm_json_new_string(parent, tmp, -1); + sub = cm_json_new_string(parent, tmp, slength); talloc_free(tmp); tmp = NULL; expect = commaorclose; diff --git a/src/json.h b/src/json.h index 63875e6..4aea0c6 100644 --- a/src/json.h +++ b/src/json.h @@ -57,7 +57,7 @@ struct cm_json *cm_json_n(struct cm_json *json, size_t n); int cm_json_append(struct cm_json *json, struct cm_json *value); int cm_json_set_n(struct cm_json *json, size_t n, struct cm_json *value); -const char *cm_json_string(struct cm_json *json); +const char *cm_json_string(struct cm_json *json, ssize_t *length); long double cm_json_numberd(struct cm_json *json); long long cm_json_numberl(struct cm_json *json); unsigned char cm_json_boolean(struct cm_json *json); diff --git a/tests/035-json/bad.2 b/tests/035-json/bad.2 new file mode 100644 index 0000000..e2c85c6 Binary files /dev/null and b/tests/035-json/bad.2 differ diff --git a/tests/035-json/expected.out b/tests/035-json/expected.out index 6f6a882..face272 100644 --- a/tests/035-json/expected.out +++ b/tests/035-json/expected.out @@ -13,6 +13,10 @@ "\\abc\"\nde\b\r/\ff\tghi\u0001\u001F" [good.15] ":\uD801\uDC1F:have a drink" +[good.16] +"-----BEGIN CERTIFICATE-----\nMIIDjjCCAnagAwIBAgIRALuVK2FuXklPuMP4qtRyQjUwDQYJKoZIhvcNAQELBQAw\nUDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRob3JpdHkxLDAqBgNVBAMMI2Ji\nOTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0MjM1MB4XDTE1MDQyODE3NTEx\nOFoXDTE2MDQyODE3NTExOFowUDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRo\nb3JpdHkxLDAqBgNVBAMMI2JiOTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0\nMjM1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzhejaX5gRYuQLRFm\n8Tq97akBa/asJPlxUrh1Vh+lKeXH+yqlPnkWoHaURn0UKj8Q26yTh3ENrqESoUrW\nJM8iR1tN4e7t1M3GGvscQOoVq7zdQ1CrGbwvd+Pdz0QTHzIEwzts2hfh6T65a0HG\nphGpS6EAxS5G8Sz8ppbqxgfynqLAYes2tBHvio0KFYpejbVzbxibE2NtMDULyF2p\nNn18Vv8aoD1I9TbSmAKsp5ToUwc4P2xKCaZ0nzlVGZaH2UXdblKc1Y8wNRs+fQ0G\nPBzjDSLYCkU52jDSFf6+lNBybHJkDDntOlRje0B0Yb+AfDO7Yl/G4h5Yth9F17mB\n68R/0wIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQEBMB0GA1UdDgQWBBQ7Udp0Ln9Z\nTac97VONJO6RYcZ4jTAfBgNVHSMEGDAWgBQ7Udp0Ln9ZTac97VONJO6RYcZ4jTAO\nBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAMHg38+LFUh86abJXZ5P\nrZE4i7Jn6mT+fIboQgZsrxn0vLXOGFBNSNhmNBp3scDY/+CzO+lE27jfGphFtJ2z\nR/vocPjS34BbNWkEBkCPv4lUYNL2yyPWM1SeOi7f/znyi7CbaAfvMz82mS8KAXep\nYsd+GvmZCMcClOXjyoSYV+3757o1n7OvOQIBnbymH+DjLE/Dcg09oahUBtV/NS0D\neGXlp39JJu6MxuAeooHDdsOPTaociNDMbJiXTC1RGqjhSXyaneZp3B01SQgMD/8N\n8nOg4kHgV181Zg4imvWK4qnaoHZeTcTgkd8MNjXeAq/OSppGQw5W5vuZ1dOTSUPR\n44A=\n-----END CERTIFICATE-----" +[good.17] +"-----BEGIN CERTIFICATE-----\nMIIDjjCCAnagAwIBAgIRALuVK2FuXklPuMP4qtRyQjUwDQYJKoZIhvcNAQELBQAw\nUDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRob3JpdHkxLDAqBgNVBAMMI2Ji\nOTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0MjM1MB4XDTE1MDQyODE3NTEx\nOFoXDTE2MDQyODE3NTExOFowUDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRo\nb3JpdHkxLDAqBgNVBAMMI2JiOTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0\nMjM1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzhejaX5gRYuQLRFm\n8Tq97akBa/asJPlxUrh1Vh+lKeXH+yqlPnkWoHaURn0UKj8Q26yTh3ENrqESoUrW\nJM8iR1tN4e7t1M3GGvscQOoVq7zdQ1CrGbwvd+Pdz0QTHzIEwzts2hfh6T65a0HG\nphGpS6EAxS5G8Sz8ppbqxgfynqLAYes2tBHvio0KFYpejbVzbxibE2NtMDULyF2p\nNn18Vv8aoD1I9TbSmAKsp5ToUwc4P2xKCaZ0nzlVGZaH2UXdblKc1Y8wNRs+fQ0G\nPBzjDSLYCkU52jDSFf6+lNBybHJkDDntOlRje0B0Yb+AfDO7Yl/G4h5Yth9F17mB\n68R/0wIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQEBMB0GA1UdDgQWBBQ7Udp0Ln9Z\nTac97VONJO6RYcZ4jTAfBgNVHSMEGDAWgBQ7Udp0Ln9ZTac97VONJO6RYcZ4jTAO\nBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAMHg38+LFUh86abJXZ5P\nrZE4i7Jn6mT+fIboQgZsrxn0vLXOGFBNSNhmNBp3scDY/+CzO+lE27jfGphFtJ2z\nR/vocPjS34BbNWkEBkCPv4lUYNL2yyPWM1SeOi7f/znyi7CbaAfvMz82mS8KAXep\nYsd+GvmZCMcClOXjyoSYV+3757o1n7OvOQIBnbymH+DjLE/Dcg09oahUBtV/NS0D\neGXlp39JJu6MxuAeooHDdsOPTaociNDMbJiXTC1RGqjhSXyaneZp3B01SQgMD/8N\n8nOg4kHgV181Zg4imvWK4qnaoHZeTcTgkd8MNjXeAq/OSppGQw5W5vuZ1dOTSUPR\n44A=\n-----END CERTIFICATE-----\n" [good.2] [{"precision":"zip","Latitude":37.766800,"Longitude":-122.395900,"Address":"","City":"SAN FRANCISCO","State":"CA","Zip":"94107","Country":"US"},{"precision":"zip","Latitude":37.371991,"Longitude":-122.026020,"Address":"","City":"SUNNYVALE","State":"CA","Zip":"94085","Country":"US"}] [good.2a] @@ -42,6 +46,7 @@ got expected error with bad.1b got expected error with bad.1c got expected error with bad.1d got expected error with bad.1e +got expected error with bad.2 got expected error with bad.8 got expected error with bad.9 OK diff --git a/tests/035-json/good.16 b/tests/035-json/good.16 new file mode 100644 index 0000000..bb27bb4 --- /dev/null +++ b/tests/035-json/good.16 @@ -0,0 +1,22 @@ +"-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIRALuVK2FuXklPuMP4qtRyQjUwDQYJKoZIhvcNAQELBQAw +UDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRob3JpdHkxLDAqBgNVBAMMI2Ji +OTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0MjM1MB4XDTE1MDQyODE3NTEx +OFoXDTE2MDQyODE3NTExOFowUDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRo +b3JpdHkxLDAqBgNVBAMMI2JiOTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0 +MjM1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzhejaX5gRYuQLRFm +8Tq97akBa/asJPlxUrh1Vh+lKeXH+yqlPnkWoHaURn0UKj8Q26yTh3ENrqESoUrW +JM8iR1tN4e7t1M3GGvscQOoVq7zdQ1CrGbwvd+Pdz0QTHzIEwzts2hfh6T65a0HG +phGpS6EAxS5G8Sz8ppbqxgfynqLAYes2tBHvio0KFYpejbVzbxibE2NtMDULyF2p +Nn18Vv8aoD1I9TbSmAKsp5ToUwc4P2xKCaZ0nzlVGZaH2UXdblKc1Y8wNRs+fQ0G +PBzjDSLYCkU52jDSFf6+lNBybHJkDDntOlRje0B0Yb+AfDO7Yl/G4h5Yth9F17mB +68R/0wIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQEBMB0GA1UdDgQWBBQ7Udp0Ln9Z +Tac97VONJO6RYcZ4jTAfBgNVHSMEGDAWgBQ7Udp0Ln9ZTac97VONJO6RYcZ4jTAO +BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAMHg38+LFUh86abJXZ5P +rZE4i7Jn6mT+fIboQgZsrxn0vLXOGFBNSNhmNBp3scDY/+CzO+lE27jfGphFtJ2z +R/vocPjS34BbNWkEBkCPv4lUYNL2yyPWM1SeOi7f/znyi7CbaAfvMz82mS8KAXep +Ysd+GvmZCMcClOXjyoSYV+3757o1n7OvOQIBnbymH+DjLE/Dcg09oahUBtV/NS0D +eGXlp39JJu6MxuAeooHDdsOPTaociNDMbJiXTC1RGqjhSXyaneZp3B01SQgMD/8N +8nOg4kHgV181Zg4imvWK4qnaoHZeTcTgkd8MNjXeAq/OSppGQw5W5vuZ1dOTSUPR +44A= +-----END CERTIFICATE-----" diff --git a/tests/035-json/good.17 b/tests/035-json/good.17 new file mode 100644 index 0000000..8d4bbbf --- /dev/null +++ b/tests/035-json/good.17 @@ -0,0 +1,23 @@ +"-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIRALuVK2FuXklPuMP4qtRyQjUwDQYJKoZIhvcNAQELBQAw +UDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRob3JpdHkxLDAqBgNVBAMMI2Ji +OTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0MjM1MB4XDTE1MDQyODE3NTEx +OFoXDTE2MDQyODE3NTExOFowUDEgMB4GA1UEAwwXTG9jYWwgU2lnbmluZyBBdXRo +b3JpdHkxLDAqBgNVBAMMI2JiOTUyYjYxLTZlNWU0OTRmLWI4YzNmOGFhLWQ0NzI0 +MjM1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzhejaX5gRYuQLRFm +8Tq97akBa/asJPlxUrh1Vh+lKeXH+yqlPnkWoHaURn0UKj8Q26yTh3ENrqESoUrW +JM8iR1tN4e7t1M3GGvscQOoVq7zdQ1CrGbwvd+Pdz0QTHzIEwzts2hfh6T65a0HG +phGpS6EAxS5G8Sz8ppbqxgfynqLAYes2tBHvio0KFYpejbVzbxibE2NtMDULyF2p +Nn18Vv8aoD1I9TbSmAKsp5ToUwc4P2xKCaZ0nzlVGZaH2UXdblKc1Y8wNRs+fQ0G +PBzjDSLYCkU52jDSFf6+lNBybHJkDDntOlRje0B0Yb+AfDO7Yl/G4h5Yth9F17mB +68R/0wIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQEBMB0GA1UdDgQWBBQ7Udp0Ln9Z +Tac97VONJO6RYcZ4jTAfBgNVHSMEGDAWgBQ7Udp0Ln9ZTac97VONJO6RYcZ4jTAO +BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAMHg38+LFUh86abJXZ5P +rZE4i7Jn6mT+fIboQgZsrxn0vLXOGFBNSNhmNBp3scDY/+CzO+lE27jfGphFtJ2z +R/vocPjS34BbNWkEBkCPv4lUYNL2yyPWM1SeOi7f/znyi7CbaAfvMz82mS8KAXep +Ysd+GvmZCMcClOXjyoSYV+3757o1n7OvOQIBnbymH+DjLE/Dcg09oahUBtV/NS0D +eGXlp39JJu6MxuAeooHDdsOPTaociNDMbJiXTC1RGqjhSXyaneZp3B01SQgMD/8N +8nOg4kHgV181Zg4imvWK4qnaoHZeTcTgkd8MNjXeAq/OSppGQw5W5vuZ1dOTSUPR +44A= +-----END CERTIFICATE----- +"