clamav/libclamav/json_api.c
Micah Snyder ae77e87880 Add EmbeddedObjects to JSON
The metadata projecties JSON structure isn't recording file types found
embedded within a file such as self-extracting (SFX) types and office
document types (DOCX, PPTX, etc).  This presents a problem...

At present there's no way to know if the current file has ended and a
few file is found tacked on to the end of the first file.  If there
were, we could simply check if the type found by the raw-scan exists
within the first file, or after.

If within the first, and the type is an archive then it's reasonable to
conclude we're either observing zip headers (for SFXZIP detections) or
other files that are not compressed.

If the type ISN'T found within the first file, then we definitely have
whole new file to parse and we should do so with cli_magic_scan()
rather than only using these embedded type scanners.

At present we can't ignore SFXZIP detections even if the original file
type is a ZIP because we may have found two ZIPs appended together to
evade detection (a legitimate trick).  As a consequence, we will
effectively parse every zip entry twice.  The same issue applies to
types found within non-compressed archives.

This commit adds an EmbeddedObjects list to the metadata JSON object so
that the existance of these types is noted.

Additionally, this commit removes the two-part int64 cli_jsonint64()
implementation as json_object_new_int64() should be available
everywhere and the macro to detect such support was never set.
2020-06-03 10:39:18 -04:00

540 lines
14 KiB
C

/*
* JSON Object API
*
* Copyright (C) 2014-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Kevin Lin
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include "clamav.h"
#include "others.h"
#include "json_api.h"
#ifdef HAVE_JSON
cl_error_t cli_json_timeout_cycle_check(cli_ctx *ctx, int *toval)
{
if (SCAN_COLLECT_METADATA) {
if (*toval <= 0) {
if (cli_checktimelimit(ctx) != CL_SUCCESS) {
cli_errmsg("cli_json_timeout_cycle_check: timeout!\n");
return CL_ETIMEOUT;
}
(*toval)++;
}
if (*toval > JSON_TIMEOUT_SKIP_CYCLES) {
(*toval) = 0;
}
}
return CL_SUCCESS;
}
cl_error_t cli_json_parse_error(json_object *root, const char *errstr)
{
json_object *perr;
if (!root)
return CL_SUCCESS; /* CL_ENULLARG? */
perr = cli_jsonarray(root, "ParseErrors");
if (perr == NULL) {
return CL_EMEM;
}
return cli_jsonstr(perr, NULL, errstr);
}
cl_error_t cli_jsonnull(json_object *obj, const char *key)
{
json_type objty;
json_object *fpobj = NULL;
if (NULL == obj) {
cli_dbgmsg("json: null 'obj' specified to cli_jsonnull\n");
return CL_ENULLARG;
}
objty = json_object_get_type(obj);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as key to cli_jsonnull\n");
return CL_ENULLARG;
}
json_object_object_add(obj, key, fpobj);
} else if (objty == json_type_array) {
json_object_array_add(obj, fpobj);
}
return CL_SUCCESS;
}
cl_error_t cli_jsonstr(json_object *obj, const char *key, const char *s)
{
json_type objty;
json_object *fpobj;
if (NULL == obj) {
cli_dbgmsg("json: null 'obj' specified to cli_jsonstr\n");
return CL_ENULLARG;
}
objty = json_object_get_type(obj);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as 'key' to cli_jsonstr\n");
return CL_ENULLARG;
}
} else if (objty != json_type_array) {
return CL_EARG;
}
if (NULL == s) {
cli_dbgmsg("json: null string specified as 's' to cli_jsonstr\n");
return CL_ENULLARG;
}
fpobj = json_object_new_string(s);
if (NULL == fpobj) {
cli_errmsg("json: no memory for json string object\n");
return CL_EMEM;
}
if (objty == json_type_object)
json_object_object_add(obj, key, fpobj);
else if (objty == json_type_array)
json_object_array_add(obj, fpobj);
return CL_SUCCESS;
}
cl_error_t cli_jsonstrlen(json_object *obj, const char *key, const char *s, int len)
{
json_type objty;
json_object *fpobj;
if (NULL == obj) {
cli_dbgmsg("json: null 'obj' specified to cli_jsonstr\n");
return CL_ENULLARG;
}
objty = json_object_get_type(obj);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as 'key' to cli_jsonstr\n");
return CL_ENULLARG;
}
} else if (objty != json_type_array) {
return CL_EARG;
}
if (NULL == s) {
cli_dbgmsg("json: null string specified as 's' to cli_jsonstr\n");
return CL_ENULLARG;
}
fpobj = json_object_new_string_len(s, len);
if (NULL == fpobj) {
cli_errmsg("json: no memory for json string object\n");
return CL_EMEM;
}
if (objty == json_type_object)
json_object_object_add(obj, key, fpobj);
else if (objty == json_type_array)
json_object_array_add(obj, fpobj);
return CL_SUCCESS;
}
cl_error_t cli_jsonint(json_object *obj, const char *key, int32_t i)
{
json_type objty;
json_object *fpobj;
if (NULL == obj) {
cli_dbgmsg("json: no parent object specified to cli_jsonint\n");
return CL_ENULLARG;
}
objty = json_object_get_type(obj);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as key to cli_jsonint\n");
return CL_ENULLARG;
}
} else if (objty != json_type_array) {
return CL_EARG;
}
fpobj = json_object_new_int(i);
if (NULL == fpobj) {
cli_errmsg("json: no memory for json int object\n");
return CL_EMEM;
}
if (objty == json_type_object)
json_object_object_add(obj, key, fpobj);
else if (objty == json_type_array)
json_object_array_add(obj, fpobj);
return CL_SUCCESS;
}
cl_error_t cli_jsonint64(json_object *obj, const char *key, int64_t i)
{
json_type objty;
json_object *fpobj;
if (NULL == obj) {
cli_dbgmsg("json: no parent object specified to cli_jsonint64\n");
return CL_ENULLARG;
}
objty = json_object_get_type(obj);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as key to cli_jsonint64\n");
return CL_ENULLARG;
}
} else if (objty != json_type_array) {
return CL_EARG;
}
fpobj = json_object_new_int64(i);
if (NULL == fpobj) {
cli_errmsg("json: no memory for json int object.\n");
return CL_EMEM;
}
if (objty == json_type_object)
json_object_object_add(obj, key, fpobj);
else if (objty == json_type_array)
json_object_array_add(obj, fpobj);
return CL_SUCCESS;
}
cl_error_t cli_jsonbool(json_object *obj, const char *key, int i)
{
json_type objty;
json_object *fpobj;
if (NULL == obj) {
cli_dbgmsg("json: no parent object specified to cli_jsonbool\n");
return CL_ENULLARG;
}
objty = json_object_get_type(obj);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as key to cli_jsonbool\n");
return CL_ENULLARG;
}
} else if (objty != json_type_array) {
return CL_EARG;
}
fpobj = json_object_new_boolean(i);
if (NULL == fpobj) {
cli_errmsg("json: no memory for json boolean object.\n");
return CL_EMEM;
}
if (objty == json_type_object)
json_object_object_add(obj, key, fpobj);
else if (objty == json_type_array)
json_object_array_add(obj, fpobj);
return CL_SUCCESS;
}
cl_error_t cli_jsondouble(json_object *obj, const char *key, double d)
{
json_type objty;
json_object *fpobj;
if (NULL == obj) {
cli_dbgmsg("json: no parent object specified to cli_jsondouble\n");
return CL_ENULLARG;
}
objty = json_object_get_type(obj);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as key to cli_jsondouble\n");
return CL_ENULLARG;
}
} else if (objty != json_type_array) {
return CL_EARG;
}
fpobj = json_object_new_double(d);
if (NULL == fpobj) {
cli_errmsg("json: no memory for json double object.\n");
return CL_EMEM;
}
if (objty == json_type_object)
json_object_object_add(obj, key, fpobj);
else if (objty == json_type_array)
json_object_array_add(obj, fpobj);
return CL_SUCCESS;
}
json_object *cli_jsonarray(json_object *obj, const char *key)
{
json_type objty;
json_object *newobj;
/* First check to see if this key exists */
if (obj && key && json_object_object_get_ex(obj, key, &newobj)) {
return json_object_is_type(newobj, json_type_array) ? newobj : NULL;
}
newobj = json_object_new_array();
if (!(newobj))
return NULL;
if (obj) {
objty = json_object_get_type(obj);
if (key && objty == json_type_object) {
json_object_object_add(obj, key, newobj);
if (!json_object_object_get_ex(obj, key, &newobj))
return NULL;
} else if (objty == json_type_array) {
json_object_array_add(obj, newobj);
}
}
return newobj;
}
cl_error_t cli_jsonint_array(json_object *obj, int32_t val)
{
return cli_jsonint(obj, NULL, val);
}
json_object *cli_jsonobj(json_object *obj, const char *key)
{
json_type objty;
json_object *newobj;
if (obj && key && json_object_object_get_ex(obj, key, &newobj))
return json_object_is_type(newobj, json_type_object) ? newobj : NULL;
newobj = json_object_new_object();
if (!(newobj))
return NULL;
if (obj) {
objty = json_object_get_type(obj);
if (key && objty == json_type_object) {
json_object_object_add(obj, key, newobj);
if (!json_object_object_get_ex(obj, key, &newobj))
return NULL;
} else if (objty == json_type_array) {
json_object_array_add(obj, newobj);
}
}
return newobj;
}
#if HAVE_DEPRECATED_JSON
int json_object_object_get_ex(struct json_object *obj, const char *key, struct json_object **value)
{
struct json_object *res;
if (value != NULL)
*value = NULL;
if (obj == NULL)
return 0;
if (json_object_get_type(obj) != json_type_object)
return 0;
res = json_object_object_get(obj, key);
if (value != NULL) {
*value = res;
return (res != NULL);
}
return (res != NULL);
}
#endif
/* adding an object does NOT increment reference count */
cl_error_t cli_json_addowner(json_object *owner, json_object *child, const char *key, int idx)
{
json_type objty;
if (NULL == owner) {
cli_dbgmsg("json: no owner object specified to cli_json_addowner\n");
return CL_ENULLARG;
}
if (NULL == child) {
cli_dbgmsg("json: no child object specified to cli_json_addowner\n");
return CL_ENULLARG;
}
objty = json_object_get_type(owner);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as key to cli_addowner\n");
return CL_ENULLARG;
}
json_object_object_add(owner, key, child);
} else if (objty == json_type_array) {
if (idx < 0 || NULL == json_object_array_get_idx(owner, idx))
json_object_array_add(owner, child);
else if (0 != json_object_array_put_idx(owner, idx, child)) {
/* this shouldn't be possible */
cli_dbgmsg("json: cannot delete idx %d of owner array\n", idx);
return CL_BREAK;
}
} else {
cli_dbgmsg("json: no owner object cannot hold ownership\n");
return CL_EARG;
}
/* increment reference count */
json_object_get(child);
return CL_SUCCESS;
}
/* deleting an object DOES decrement reference count */
cl_error_t cli_json_delowner(json_object *owner, const char *key, int idx)
{
json_type objty;
json_object *obj;
if (NULL == owner) {
cli_dbgmsg("json: no owner object specified to cli_json_delowner\n");
return CL_ENULLARG;
}
objty = json_object_get_type(owner);
if (objty == json_type_object) {
if (NULL == key) {
cli_dbgmsg("json: null string specified as key to cli_delowner\n");
return CL_ENULLARG;
}
if (!json_object_object_get_ex(owner, key, &obj)) {
cli_dbgmsg("json: owner array does not have content with key %s\n", key);
return CL_EARG;
}
json_object_object_del(owner, key);
} else if (objty == json_type_array) {
json_object *empty;
if (NULL == json_object_array_get_idx(owner, idx)) {
cli_dbgmsg("json: owner array does not have content at idx %d\n", idx);
return CL_EARG;
}
/* allocate the empty object to replace target object */
empty = cli_jsonobj(NULL, NULL);
if (NULL == empty)
return CL_EMEM;
if (0 != json_object_array_put_idx(owner, idx, empty)) {
/* this shouldn't be possible */
cli_dbgmsg("json: cannot delete idx %d of owner array\n", idx);
return CL_BREAK;
}
} else {
cli_dbgmsg("json: no owner object cannot hold ownership\n");
return CL_EARG;
}
return CL_SUCCESS;
}
#else
cl_error_t cli_json_nojson()
{
nojson_func("nojson: json needs to be enabled for this feature\n");
return CL_SUCCESS;
}
cl_error_t cli_jsonnull_nojson(const char* key)
{
nojson_func("nojson: %s: null\n", key);
return CL_SUCCESS;
}
cl_error_t cli_jsonstr_nojson(const char* key, const char* s)
{
nojson_func("nojson: %s: %s\n", key, s);
return CL_SUCCESS;
}
cl_error_t cli_jsonstrlen_nojson(const char* key, const char* s, int len)
{
char* sp = cli_malloc(len + 1);
if (NULL == sp) {
cli_errmsg("json: no memory for json strlen object.\n");
return CL_EMEM;
}
strncpy(sp, s, len);
sp[len] = '\0';
nojson_func("nojson: %s: %s\n", key, sp);
free(sp);
return CL_SUCCESS;
}
cl_error_t cli_jsonint_nojson(const char* key, int32_t i)
{
nojson_func("nojson: %s: %d\n", key, i);
return CL_SUCCESS;
}
cl_error_t cli_jsonint64_nojson(const char* key, int64_t i)
{
nojson_func("nojson: %s: %ld\n", key, (long int)i);
return CL_SUCCESS;
}
cl_error_t cli_jsonbool_nojson(const char* key, int i)
{
nojson_func("nojson: %s: %s\n", key, i ? "true" : "false");
return CL_SUCCESS;
}
cl_error_t cli_jsondouble_nojson(const char* key, double d)
{
nojson_func("nojson: %s: %f\n", key, d);
return CL_SUCCESS;
}
void* cli_jsonarray_nojson(const char* key)
{
nojson_func("nojson: %s\n", key);
return NULL;
}
cl_error_t cli_jsonint_array_nojson(int32_t val)
{
nojson_func("nojson: %d\n", val);
return CL_SUCCESS;
}
#endif