Merge pull request #2878 from fengzeroz/main

limit path & fix memory check
This commit is contained in:
fengzeroz
2026-07-02 14:36:48 +08:00
committed by GitHub
25 changed files with 451 additions and 80 deletions
+1
View File
@@ -103,6 +103,7 @@ set(NEURON_BASE_SOURCES
src/utils/cid.c
src/utils/ede.c
src/utils/tpy.c
src/utils/neu_path.c
${PERSIST_SOURCES})
if (SMART_LINK)
+1
View File
@@ -38,6 +38,7 @@ extern "C" {
#include "utils/zlog.h"
#include "utils/neu_jwt.h"
#include "utils/neu_path.h"
#include "utils/time.h"
#include "utils/utarray.h"
#include "utils/utextend.h"
+50
View File
@@ -0,0 +1,50 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2024 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
**/
#ifndef NEURON_UTILS_PATH_H
#define NEURON_UTILS_PATH_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
// Reject an untrusted path component (e.g. a node/plugin/library name that is
// used to build a filesystem path). Returns true only when `name` is a single,
// safe path segment: non-empty, contains no '/' and is not "." or "..".
bool neu_path_is_valid_component(const char *name);
// Confine an untrusted `path` to the trusted base directory `base`.
//
// The result is the canonical absolute path when it resolves inside `base`
// (either a relative path joined under `base`, or an absolute path that already
// lies within `base`). Returns a newly heap-allocated string the caller must
// free(), or NULL if the path escapes `base`, is invalid, or cannot be
// resolved. A not-yet-existing final component (e.g. a file about to be
// created) is handled by resolving its parent directory.
//
// If `base` is NULL or empty, the current working directory is used.
char *neu_path_confine(const char *base, const char *path);
#ifdef __cplusplus
}
#endif
#endif
+12 -6
View File
@@ -181,7 +181,9 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
{
neu_dvalue_t dvalue = { 0 };
FILE *fp = fopen(tag->address, "r");
char *safe_path = neu_path_confine(NULL, tag->address);
FILE *fp = safe_path ? fopen(safe_path, "r") : NULL;
free(safe_path);
if (fp == NULL) {
dvalue.type = NEU_TYPE_ERROR;
if (errno == ENOENT) {
@@ -190,14 +192,16 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
dvalue.value.i32 = NEU_ERR_FILE_OPEN_FAILURE;
}
} else {
char *buf = calloc(1, 1024);
int ret = fread(buf, 1, 1024, fp);
if (ret < 0) {
char * buf = calloc(1, 1024);
size_t ret = fread(buf, 1, 1024, fp);
if (ret == 0 && ferror(fp)) {
dvalue.type = NEU_TYPE_ERROR;
dvalue.value.i32 = NEU_ERR_FILE_READ_FAILURE;
} else {
size_t n = ret < NEU_VALUE_SIZE - 1 ? ret : NEU_VALUE_SIZE - 1;
dvalue.type = NEU_TYPE_STRING;
strncpy(dvalue.value.str, buf, NEU_VALUE_SIZE - 1);
strncpy(dvalue.value.str, buf, n);
dvalue.value.str[n] = '\0';
}
fclose(fp);
@@ -219,7 +223,9 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
neu_value_u value)
{
FILE *fp = fopen(tag->address, "w");
char *safe_path = neu_path_confine(NULL, tag->address);
FILE *fp = safe_path ? fopen(safe_path, "w") : NULL;
free(safe_path);
if (fp == NULL) {
if (errno == ENOENT) {
plugin->common.adapter_callbacks->driver.write_response(
+18 -4
View File
@@ -41,9 +41,11 @@ static char *generate_heartbeat_json(neu_plugin_t *plugin, UT_array *states)
char * json_str = NULL;
json.n_state = utarray_len(states);
json.states = calloc(json.n_state, sizeof(neu_json_node_state_t));
if (NULL == json.states) {
return NULL;
if (json.n_state > 0) {
json.states = calloc(json.n_state, sizeof(neu_json_node_state_t));
if (NULL == json.states) {
return NULL;
}
}
utarray_foreach(states, neu_nodes_state_t *, state)
@@ -210,6 +212,10 @@ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event,
json_req.add_gtag.n_group = add_gtag->n_group;
json_req.add_gtag.groups =
calloc(add_gtag->n_group, sizeof(*json_req.add_gtag.groups));
if (NULL == json_req.add_gtag.groups) {
plog_error(plugin, "calloc fail");
break;
}
for (u_int16_t i = 0; i < add_gtag->n_group; i++) {
json_req.add_gtag.groups[i].group =
strdup(add_gtag->groups[i].group);
@@ -218,6 +224,10 @@ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event,
json_req.add_gtag.groups[i].tags =
calloc(add_gtag->groups[i].n_tag,
sizeof(*json_req.add_gtag.groups->tags));
if (NULL == json_req.add_gtag.groups[i].tags) {
plog_error(plugin, "calloc fail");
break;
}
for (u_int16_t j = 0; j < add_gtag->groups[i].n_tag; j++) {
json_req.add_gtag.groups[i].tags[j].type =
add_gtag->groups[i].tags[j].type;
@@ -239,7 +249,11 @@ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event,
neu_json_encode_by_fn(&json_req, neu_json_encode_add_gtags_req,
&json_str);
free(json_req.add_tags.tags);
for (u_int16_t i = 0; i < add_gtag->n_group; i++) {
free(json_req.add_gtag.groups[i].group);
free(json_req.add_gtag.groups[i].tags);
}
free(json_req.add_gtag.groups);
break;
}
case NEU_REQ_ADD_PLUGIN_EVENT: {
+35 -3
View File
@@ -604,7 +604,12 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic,
}
}
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
if (NULL == mqtt) {
plog_error(plugin, "calloc mqtt fail");
model__write_request__free_unpacked(wr, NULL);
return;
}
mqtt->uuid = strdup(wr->uuid);
mqtt->payload = NULL;
mqtt->traceparent = NULL;
@@ -620,6 +625,14 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic,
cmd.n_tag = wr->n_tags;
if (cmd.n_tag > 0) {
cmd.tags = calloc(cmd.n_tag, sizeof(neu_resp_tag_value_t));
if (NULL == cmd.tags) {
plog_error(plugin, "calloc cmd.tags fail");
free(cmd.driver);
free(cmd.group);
neu_json_decode_mqtt_req_free(mqtt);
model__write_request__free_unpacked(wr, NULL);
return;
}
}
for (int i = 0; i < cmd.n_tag; i++) {
@@ -930,7 +943,12 @@ void handle_driver_action_req(neu_mqtt_qos_e qos, const char *topic,
return;
}
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
if (NULL == mqtt) {
plog_error(plugin, "calloc mqtt fail");
model__driver_action_request__free_unpacked(dar, NULL);
return;
}
mqtt->uuid = strdup(dar->uuid);
mqtt->payload = NULL;
mqtt->traceparent = NULL;
@@ -1004,7 +1022,12 @@ void handle_read_req(neu_mqtt_qos_e qos, const char *topic,
neu_reqresp_head_t header = { 0 };
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
if (NULL == mqtt) {
plog_error(plugin, "calloc mqtt fail");
model__read_request__free_unpacked(read_req, NULL);
return;
}
mqtt->uuid = strdup(read_req->uuid);
mqtt->payload = NULL;
mqtt->traceparent = NULL;
@@ -1021,6 +1044,14 @@ void handle_read_req(neu_mqtt_qos_e qos, const char *topic,
cmd.n_tag = read_req->n_tags;
if (cmd.n_tag > 0) {
cmd.tags = calloc(cmd.n_tag, sizeof(char *));
if (NULL == cmd.tags) {
plog_error(plugin, "calloc cmd.tags fail");
free(cmd.driver);
free(cmd.group);
neu_json_decode_mqtt_req_free(mqtt);
model__read_request__free_unpacked(read_req, NULL);
return;
}
}
for (int i = 0; i < cmd.n_tag; i++) {
cmd.tags[i] = strdup(read_req->tags[i]);
@@ -1029,6 +1060,7 @@ void handle_read_req(neu_mqtt_qos_e qos, const char *topic,
if (0 != neu_plugin_op(plugin, header, &cmd)) {
neu_req_read_group_fini(&cmd);
plog_error(plugin, "neu_plugin_op(NEU_REQ_READ_GROUP) fail");
model__read_request__free_unpacked(read_req, NULL);
return;
}
+3 -1
View File
@@ -706,7 +706,9 @@ void handle_put_node_tag(nng_aio *aio)
header.ctx = aio;
header.type = NEU_REQ_UPDATE_NODE_TAG;
strncpy(cmd.node, req->name, NEU_NODE_NAME_LEN - 1);
strncpy(cmd.tags, req->tags, NEU_NODE_TAGS_LEN - 1);
if (req->tags != NULL) {
strncpy(cmd.tags, req->tags, NEU_NODE_TAGS_LEN - 1);
}
int ret = neu_plugin_op(plugin, header, &cmd);
if (ret != 0) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, {
+22 -2
View File
@@ -49,8 +49,15 @@ static void update_timestamp(neu_group_t *group);
neu_group_t *neu_group_new(const char *name, uint32_t interval)
{
neu_group_t *group = calloc(1, sizeof(neu_group_t));
if (NULL == group) {
return NULL;
}
group->name = strdup(name);
group->name = strdup(name);
if (NULL == group->name) {
free(group);
return NULL;
}
group->interval = interval;
pthread_mutex_init(&group->mtx, NULL);
@@ -142,9 +149,22 @@ int neu_group_add_tag(neu_group_t *group, const neu_datatag_t *tag)
return NEU_ERR_TAG_NAME_CONFLICT;
}
el = calloc(1, sizeof(tag_elem_t));
el = calloc(1, sizeof(tag_elem_t));
if (NULL == el) {
pthread_mutex_unlock(&group->mtx);
return NEU_ERR_EINTERNAL;
}
el->name = strdup(tag->name);
el->tag = neu_tag_dup(tag);
if (NULL == el->name || NULL == el->tag) {
free(el->name);
if (el->tag) {
neu_tag_free(el->tag);
}
free(el);
pthread_mutex_unlock(&group->mtx);
return NEU_ERR_EINTERNAL;
}
HASH_ADD_STR(group->tags, name, el);
update_timestamp(group);
+4 -3
View File
@@ -76,7 +76,7 @@ static void find_os_info()
}
buf[strcspn(buf, "\n")] = 0;
strncpy(g_metrics_.machine, buf, sizeof(g_metrics_.machine));
g_metrics_.kernel[sizeof(g_metrics_.machine) - 1] = 0;
g_metrics_.machine[sizeof(g_metrics_.machine) - 1] = 0;
pclose(f);
@@ -87,6 +87,7 @@ static void find_os_info()
strncpy(g_metrics_.clib, "glibc", sizeof(g_metrics_.clib));
strncpy(g_metrics_.clib_version, gnu_get_libc_version(),
sizeof(g_metrics_.clib_version));
g_metrics_.clib_version[sizeof(g_metrics_.clib_version) - 1] = 0;
#endif
}
@@ -96,7 +97,7 @@ static size_t parse_memory_fields(int col)
char buf[64] = {};
size_t val = 0;
sprintf(buf, "free -b | awk 'NR==2 {print $%i}'", col);
snprintf(buf, sizeof(buf), "free -b | awk 'NR==2 {print $%i}'", col);
f = popen(buf, "r");
if (NULL == f) {
@@ -131,7 +132,7 @@ static inline size_t neuron_memory_used()
size_t val = 0;
pid_t pid = getpid();
sprintf(buf, "ps -o rss= %ld", (long) pid);
snprintf(buf, sizeof(buf), "ps -o rss= %ld", (long) pid);
f = popen(buf, "r");
if (NULL == f) {
+6 -3
View File
@@ -37,9 +37,9 @@ static void tag_array_copy(void *_dst, const void *_src)
dst->decimal = src->decimal;
dst->bias = src->bias;
dst->option = src->option;
dst->address = strdup(src->address);
dst->name = strdup(src->name);
dst->description = strdup(src->description);
dst->address = strdup(src->address ? src->address : "");
dst->name = strdup(src->name ? src->name : "");
dst->description = strdup(src->description ? src->description : "");
if (src->unit == NULL) {
dst->unit = strdup("");
} else {
@@ -107,6 +107,9 @@ UT_icd *neu_tag_get_icd()
neu_datatag_t *neu_tag_dup(const neu_datatag_t *tag)
{
neu_datatag_t *new = calloc(1, sizeof(*new));
if (NULL == new) {
return NULL;
}
tag_array_copy(new, tag);
return new;
}
+3
View File
@@ -1408,6 +1408,9 @@ int neu_conn_stream_tcp_server_consume(neu_conn_t *conn, int fd, void *context,
int neu_conn_wait_msg(neu_conn_t *conn, void *context, uint16_t n_byte,
neu_conn_process_msg fn)
{
if (n_byte > conn->buf_size) {
n_byte = conn->buf_size;
}
ssize_t ret = neu_conn_recv(conn, conn->buf, n_byte);
neu_protocol_unpack_buf_t pbuf = { 0 };
conn->offset = 0;
+16
View File
@@ -31,6 +31,7 @@
#include "utils/base64.h"
#include "utils/http.h"
#include "utils/log.h"
#include "utils/neu_path.h"
#include "utils/time.h"
#include "adapter.h"
@@ -303,6 +304,21 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
char schema[64] = { 0 };
char buffer[65] = { 0 };
// The library name is used to build a path under the plugin dir; reject
// any name with a path separator or '..' so it cannot escape that dir.
if (!neu_path_is_valid_component(cmd->library)) {
nlog_warn("reject library name with path separator: %s",
cmd->library);
free(cmd->so_file);
free(cmd->schema_file);
header->type = NEU_RESP_ERROR;
e.error = NEU_ERR_LIBRARY_NAME_NOT_CONFORM;
snprintf(header->receiver, sizeof(header->receiver), "%s",
header->sender);
reply(manager, header, &e);
break;
}
if (sscanf(cmd->library, "libplugin-%64s.so", buffer) != 1) {
nlog_warn("library %s no conform", cmd->library);
free(cmd->so_file);
+15
View File
@@ -785,7 +785,15 @@ void neu_otel_scope_set_parent_span_id2(neu_otel_scope_ctx ctx,
{
trace_scope_t *scope = (trace_scope_t *) ctx;
if (len < 0) {
len = 0;
} else if (len > 8) {
len = 8; // parent_span_id buffer is 8 bytes
}
uint8_t *p_sp_id = calloc(1, 8);
if (NULL == p_sp_id) {
return;
}
memcpy(p_sp_id, parent_span_id, len);
scope->span->parent_span_id.len = len;
scope->span->parent_span_id.data = p_sp_id;
@@ -1036,6 +1044,13 @@ void neu_otel_split_traceparent(const char *in, char *trace_id, char *span_id,
const size_t trace_id_cap = 64;
const size_t span_id_cap = 32;
if (NULL == copy) {
trace_id[0] = '\0';
span_id[0] = '\0';
*flags = 0;
return;
}
token = strtok_r(copy, delimiter, &saveptr);
token = strtok_r(NULL, delimiter, &saveptr);
+26
View File
@@ -600,6 +600,12 @@ static int decode_write_tags_req_json(void * json_obj,
}
req->tags = calloc(req->n_tag, sizeof(neu_json_write_tags_elem_t));
if (NULL == req->tags) {
req->n_tag = 0;
free(req->node);
free(req->group);
return -1;
}
for (int i = 0; i < req->n_tag; i++) {
neu_json_elem_t v_elems[] = {
{
@@ -1315,6 +1321,11 @@ static int decode_write_gtags_req_json(void * json_obj,
}
req->groups = calloc(req->n_group, sizeof(neu_json_write_gtags_elem_t));
if (NULL == req->groups) {
req->n_group = 0;
free(req->node);
return -1;
}
for (int i = 0; i < req->n_group; i++) {
neu_json_elem_t g_elems[] = {
{
@@ -1329,6 +1340,13 @@ static int decode_write_gtags_req_json(void * json_obj,
ret = neu_json_decode_array_by_json(
json_obj, "groups", i, NEU_JSON_ELEM_SIZE(g_elems), g_elems);
if (ret != 0) {
for (int x = i - 1; x >= 0; x--) {
free(req->groups[x].tags);
}
free(req->groups);
return -1;
}
req->groups[i].group = g_elems[0].v.val_str;
req->groups[i].n_tag = json_array_size(g_elems[1].v.val_object);
@@ -1338,6 +1356,14 @@ static int decode_write_gtags_req_json(void * json_obj,
req->groups[i].tags =
calloc(req->groups[i].n_tag, sizeof(neu_json_write_tags_elem_t));
if (NULL == req->groups[i].tags) {
req->groups[i].n_tag = 0;
for (int x = i - 1; x >= 0; x--) {
free(req->groups[x].tags);
}
free(req->groups);
return -1;
}
for (int k = 0; k < req->groups[i].n_tag; k++) {
neu_json_elem_t v_elems[] = {
+6 -2
View File
@@ -259,7 +259,7 @@ int neu_json_decode_tag_array_json(void *json_obj, neu_json_tag_array_t *arr)
return 0;
decode_fail:
while (--i > 0) {
while (i-- > 0) {
neu_json_decode_tag_fini(&tags[i]);
}
free(tags);
@@ -595,7 +595,7 @@ int neu_json_decode_gtag_array_json(void *json_obj, neu_json_gtag_array_t *arr)
return 0;
decode_fail:
while (--i > 0) {
while (i-- > 0) {
neu_json_decode_gtag_fini(&gtags[i]);
}
free(gtags);
@@ -814,6 +814,10 @@ int neu_json_decode_del_tags_req(char *buf, neu_json_del_tags_req_t **result)
}
req->tags = calloc(req->n_tags, sizeof(neu_json_del_tags_req_name_t));
if (NULL == req->tags) {
req->n_tags = 0;
goto decode_fail;
}
neu_json_del_tags_req_name_t *p_tag = req->tags;
for (int i = 0; i < req->n_tags; i++) {
neu_json_elem_t id_elems[] = { {
+59 -25
View File
@@ -536,12 +536,15 @@ int neu_sqlite_persister_load_nodes(neu_persister_t *self,
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
neu_persist_node_info_t info = {};
char *name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *name_txt = (const char *) sqlite3_column_text(stmt, 0);
char * name = name_txt ? strdup(name_txt) : NULL;
if (NULL == name) {
break;
}
char *plugin_name = strdup((char *) sqlite3_column_text(stmt, 3));
const char *plugin_name_txt =
(const char *) sqlite3_column_text(stmt, 3);
char *plugin_name = plugin_name_txt ? strdup(plugin_name_txt) : NULL;
if (NULL == plugin_name) {
free(name);
break;
@@ -971,12 +974,16 @@ int neu_sqlite_persister_load_subscriptions(neu_persister_t *self,
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
char *driver_name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *driver_name_txt =
(const char *) sqlite3_column_text(stmt, 0);
char *driver_name = driver_name_txt ? strdup(driver_name_txt) : NULL;
if (NULL == driver_name) {
break;
}
char *group_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *group_name_txt =
(const char *) sqlite3_column_text(stmt, 1);
char *group_name = group_name_txt ? strdup(group_name_txt) : NULL;
if (NULL == group_name) {
free(driver_name);
break;
@@ -1091,7 +1098,8 @@ static int collect_group_info(sqlite3_stmt *stmt, UT_array **group_infos)
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
neu_persist_group_info_t info = {};
char *name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *name_txt = (const char *) sqlite3_column_text(stmt, 0);
char * name = name_txt ? strdup(name_txt) : NULL;
if (NULL == name) {
break;
}
@@ -1207,7 +1215,8 @@ int neu_sqlite_persister_load_node_setting(neu_persister_t * self,
goto end;
}
char *s = strdup((char *) sqlite3_column_text(stmt, 0));
const char *setting_txt = (const char *) sqlite3_column_text(stmt, 0);
char * s = setting_txt ? strdup(setting_txt) : NULL;
if (NULL == s) {
nlog_error("strdup fail");
rv = NEU_ERR_EINTERNAL;
@@ -1240,14 +1249,17 @@ static int collect_user_info(sqlite3_stmt *stmt, UT_array **user_infos)
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
neu_persist_user_info_t info = {};
char *name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *name_txt = (const char *) sqlite3_column_text(stmt, 0);
char * name = name_txt ? strdup(name_txt) : NULL;
if (NULL == name) {
break;
}
info.name = name;
info.hash = strdup((char *) sqlite3_column_text(stmt, 1));
info.name = name;
const char *hash_txt = (const char *) sqlite3_column_text(stmt, 1);
info.hash = hash_txt ? strdup(hash_txt) : NULL;
if (NULL == info.hash) {
free(name);
break;
}
utarray_push_back(*user_infos, &info);
@@ -1342,7 +1354,8 @@ int neu_sqlite_persister_load_user(neu_persister_t *self, const char *user_name,
goto error;
}
user->hash = strdup((char *) sqlite3_column_text(stmt, 0));
const char *hash_txt = (const char *) sqlite3_column_text(stmt, 0);
user->hash = hash_txt ? strdup(hash_txt) : NULL;
if (NULL == user->hash) {
nlog_error("strdup fail");
goto error;
@@ -1481,8 +1494,10 @@ int neu_sqlite_persister_load_server_cert(
// Read all fields
cert_info->id = sqlite3_column_int(stmt, 0);
cert_info->app_name = strdup((char *) sqlite3_column_text(stmt, 1));
cert_info->common_name = strdup((char *) sqlite3_column_text(stmt, 2));
char *app_name_db = (char *) sqlite3_column_text(stmt, 1);
cert_info->app_name = app_name_db ? strdup(app_name_db) : NULL;
char *common_name = (char *) sqlite3_column_text(stmt, 2);
cert_info->common_name = common_name ? strdup(common_name) : NULL;
char *subject = (char *) sqlite3_column_text(stmt, 3);
cert_info->subject = subject ? strdup(subject) : NULL;
@@ -1543,13 +1558,16 @@ static int collect_client_cert_info(sqlite3_stmt *stmt, UT_array **cert_infos)
info.id = sqlite3_column_int(stmt, 0);
char *app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_col = (const char *) sqlite3_column_text(stmt, 1);
char * app_name = app_name_col ? strdup(app_name_col) : NULL;
if (NULL == app_name) {
break;
}
info.app_name = app_name;
char *common_name = strdup((char *) sqlite3_column_text(stmt, 2));
const char *common_name_col =
(const char *) sqlite3_column_text(stmt, 2);
char *common_name = common_name_col ? strdup(common_name_col) : NULL;
if (NULL == common_name) {
free(app_name);
break;
@@ -1838,7 +1856,8 @@ int neu_sqlite_persister_load_security_policy(
info->id = sqlite3_column_int(stmt, 0);
char *app_name_col = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_txt = (const char *) sqlite3_column_text(stmt, 1);
char * app_name_col = app_name_txt ? strdup(app_name_txt) : NULL;
if (NULL == app_name_col) {
free(info);
sqlite3_finalize(stmt);
@@ -1846,7 +1865,9 @@ int neu_sqlite_persister_load_security_policy(
}
info->app_name = app_name_col;
char *policy_name = strdup((char *) sqlite3_column_text(stmt, 2));
const char *policy_name_txt =
(const char *) sqlite3_column_text(stmt, 2);
char *policy_name = policy_name_txt ? strdup(policy_name_txt) : NULL;
if (NULL == policy_name) {
free(info->app_name);
free(info);
@@ -1899,13 +1920,16 @@ static int collect_security_policy_info(sqlite3_stmt *stmt,
info.id = sqlite3_column_int(stmt, 0);
char *app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_txt = (const char *) sqlite3_column_text(stmt, 1);
char * app_name = app_name_txt ? strdup(app_name_txt) : NULL;
if (NULL == app_name) {
break;
}
info.app_name = app_name;
char *policy_name = strdup((char *) sqlite3_column_text(stmt, 2));
const char *policy_name_txt =
(const char *) sqlite3_column_text(stmt, 2);
char *policy_name = policy_name_txt ? strdup(policy_name_txt) : NULL;
if (NULL == policy_name) {
free(app_name);
break;
@@ -2027,7 +2051,8 @@ int neu_sqlite_persister_load_auth_setting(
auth_info->id = sqlite3_column_int(stmt, 0);
auth_info->enabled = sqlite3_column_int(stmt, 2);
auth_info->app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *auth_app_name = (const char *) sqlite3_column_text(stmt, 1);
auth_info->app_name = auth_app_name ? strdup(auth_app_name) : NULL;
if (NULL == auth_info->app_name) {
nlog_error("strdup fail");
goto error;
@@ -2064,20 +2089,25 @@ static int collect_auth_user_info(sqlite3_stmt *stmt, UT_array **user_infos)
info.id = sqlite3_column_int(stmt, 0);
char *app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_txt = (const char *) sqlite3_column_text(stmt, 1);
char * app_name = app_name_txt ? strdup(app_name_txt) : NULL;
if (NULL == app_name) {
break;
}
info.app_name = app_name;
char *username = strdup((char *) sqlite3_column_text(stmt, 2));
const char *username_txt = (const char *) sqlite3_column_text(stmt, 2);
char * username = username_txt ? strdup(username_txt) : NULL;
if (NULL == username) {
free(app_name);
break;
}
info.username = username;
char *password_hash = strdup((char *) sqlite3_column_text(stmt, 3));
const char *password_hash_txt =
(const char *) sqlite3_column_text(stmt, 3);
char *password_hash =
password_hash_txt ? strdup(password_hash_txt) : NULL;
if (NULL == password_hash) {
free(app_name);
free(username);
@@ -2170,19 +2200,23 @@ int neu_sqlite_persister_load_auth_user(
user_info->id = sqlite3_column_int(stmt, 0);
user_info->app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *ui_app_name = (const char *) sqlite3_column_text(stmt, 1);
user_info->app_name = ui_app_name ? strdup(ui_app_name) : NULL;
if (NULL == user_info->app_name) {
nlog_error("strdup fail");
goto error;
}
user_info->username = strdup((char *) sqlite3_column_text(stmt, 2));
const char *ui_username = (const char *) sqlite3_column_text(stmt, 2);
user_info->username = ui_username ? strdup(ui_username) : NULL;
if (NULL == user_info->username) {
nlog_error("strdup fail");
goto error;
}
user_info->password_hash = strdup((char *) sqlite3_column_text(stmt, 3));
const char *ui_password_hash = (const char *) sqlite3_column_text(stmt, 3);
user_info->password_hash =
ui_password_hash ? strdup(ui_password_hash) : NULL;
if (NULL == user_info->password_hash) {
nlog_error("strdup fail");
goto error;
+6
View File
@@ -18,6 +18,7 @@
**/
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -57,6 +58,11 @@ int Base64Decode(const char *b64message, unsigned char **buffer, int *length)
{ // Decodes a base64 encoded string
BIO *bio, *b64;
// BIO_read() takes an int length; reject oversized input so neither the
// read length nor the derived decodeLen can overflow/truncate to int.
if (strlen(b64message) > INT_MAX) {
return -1;
}
int decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char *) malloc(decodeLen + 1);
if (NULL == *buffer) {
+16 -2
View File
@@ -24,6 +24,7 @@
#include "define.h"
#include "utils/cid.h"
#include "utils/log.h"
#include "utils/neu_path.h"
#include "json/json.h"
static int parse_ied(xmlNode *xml_ied, cid_ied_t *ied);
@@ -40,7 +41,14 @@ static void fill_doi_ctls(cid_t *cid);
int neu_cid_parse(const char *path, cid_t *cid)
{
memset(cid, 0, sizeof(cid_t));
xmlDoc *doc = xmlReadFile(path, NULL, 0);
char *safe_path = neu_path_confine(NULL, path);
if (safe_path == NULL) {
nlog_warn("reject icd file path outside working dir: %s", path);
return -1;
}
// XML_PARSE_NONET blocks network access for external entities/DTDs (XXE).
xmlDoc *doc = xmlReadFile(safe_path, NULL, XML_PARSE_NONET);
free(safe_path);
if (doc == NULL) {
nlog_warn("Failed to read icd file %s", path);
return -1;
@@ -1059,8 +1067,14 @@ static int parse_template(xmlNode *xml_template, cid_template_t *template)
}
if (count != NULL) {
int cnt = atoi(count);
if (cnt < 0) {
cnt = 0;
} else if (cnt > 255) {
cnt = 255; // array_size is uint8_t
}
tm_da->is_array = true;
tm_da->array_size = atoi(count);
tm_da->array_size = (uint8_t) cnt;
} else {
tm_da->is_array = false;
tm_da->array_size = 0;
+14 -1
View File
@@ -24,6 +24,7 @@
#include <string.h>
#include "utils/ede.h"
#include "utils/neu_path.h"
typedef struct {
int object_type;
@@ -242,7 +243,12 @@ int neu_ede_parse_file(const char *file_path, neu_ede_result_t *result)
result->entries = NULL;
result->count = 0;
fp = fopen(file_path, "r");
char *safe_path = neu_path_confine(NULL, file_path);
if (safe_path == NULL) {
return -1;
}
fp = fopen(safe_path, "r");
free(safe_path);
if (fp == NULL) {
return -1;
}
@@ -359,6 +365,13 @@ int neu_ede_parse_file_to_tags(const char *file_path, neu_datatag_t **tags,
return -1;
}
if (result.count == 0) {
neu_ede_result_uninit(&result);
*tags = NULL;
*count = 0;
return 0;
}
out = calloc(result.count, sizeof(neu_datatag_t));
if (out == NULL) {
neu_ede_result_uninit(&result);
+3
View File
@@ -67,6 +67,9 @@ ssize_t neu_url_decode(const char *s, size_t len, char *buf, size_t size)
size_t i = 0, j = 0;
int n = 0;
unsigned int c;
if (size == 0) {
return -1;
}
while (i < len && j < size) {
c = s[i++];
if ('+' == c) {
+122
View File
@@ -0,0 +1,122 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2024 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
**/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils/neu_path.h"
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
bool neu_path_is_valid_component(const char *name)
{
if (name == NULL || name[0] == '\0') {
return false;
}
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
return false;
}
// A safe component must not contain a path separator.
if (strchr(name, '/') != NULL) {
return false;
}
return true;
}
// Return true if `resolved` is `base` or lies below `base/`.
static bool is_within(const char *base, const char *resolved)
{
size_t bl = strlen(base);
if (strncmp(resolved, base, bl) != 0) {
return false;
}
// Exact match, or the next char is a separator (a real sub-path).
return resolved[bl] == '\0' || resolved[bl] == '/';
}
char *neu_path_confine(const char *base, const char *path)
{
if (path == NULL || path[0] == '\0') {
return NULL;
}
char base_real[PATH_MAX];
if (base == NULL || base[0] == '\0') {
base = ".";
}
if (realpath(base, base_real) == NULL) {
return NULL;
}
// Build an absolute candidate: absolute paths are taken as-is (and must
// still resolve inside base), relative paths are joined under base.
char candidate[PATH_MAX];
if (path[0] == '/') {
if (strlen(path) >= sizeof(candidate)) {
return NULL;
}
strcpy(candidate, path);
} else {
if ((size_t) snprintf(candidate, sizeof(candidate), "%s/%s", base_real,
path) >= sizeof(candidate)) {
return NULL;
}
}
char resolved[PATH_MAX];
if (realpath(candidate, resolved) == NULL) {
// The final component may not exist yet (e.g. a file about to be
// created). Resolve the parent directory and re-append the basename.
char tmp[PATH_MAX];
strncpy(tmp, candidate, sizeof(tmp));
tmp[sizeof(tmp) - 1] = '\0';
char *slash = strrchr(tmp, '/');
if (slash == NULL) {
return NULL;
}
*slash = '\0';
const char *bname = slash + 1;
if (!neu_path_is_valid_component(bname)) {
return NULL;
}
char parent_real[PATH_MAX];
if (realpath(tmp[0] != '\0' ? tmp : "/", parent_real) == NULL) {
return NULL;
}
if (!is_within(base_real, parent_real)) {
return NULL;
}
if ((size_t) snprintf(resolved, sizeof(resolved), "%s/%s", parent_real,
bname) >= sizeof(resolved)) {
return NULL;
}
return strdup(resolved);
}
if (!is_within(base_real, resolved)) {
return NULL;
}
return strdup(resolved);
}
+11 -2
View File
@@ -20,6 +20,7 @@
#include <libxml/tree.h>
#include "utils/log.h"
#include "utils/neu_path.h"
#include "utils/tpy.h"
static int parse_vars(xmlNode *xml_ied, tpy_t *tpy);
@@ -27,7 +28,14 @@ static int parse_vars(xmlNode *xml_ied, tpy_t *tpy);
int neu_tpy_parse(const char *path, tpy_t *tpy)
{
memset(tpy, 0, sizeof(tpy_t));
xmlDoc *doc = xmlReadFile(path, NULL, 0);
char *safe_path = neu_path_confine(NULL, path);
if (safe_path == NULL) {
nlog_warn("reject tpy file path outside working dir: %s", path);
return -1;
}
// XML_PARSE_NONET blocks network access for external entities/DTDs (XXE).
xmlDoc *doc = xmlReadFile(safe_path, NULL, XML_PARSE_NONET);
free(safe_path);
if (doc == NULL) {
nlog_warn("Failed to read tpy file %s", path);
return -1;
@@ -101,7 +109,8 @@ static int parse_vars(xmlNode *xml_ied, tpy_t *tpy)
attr, (const xmlChar *) "TaskPrio");
snprintf(static_name, sizeof(static_name), "%s_%s",
name_static, name_task);
name_static != NULL ? name_static : "",
name_task != NULL ? name_task : "");
strncpy(tpy_var.name, content,
sizeof(tpy_var.name) - 1);
xmlFree(name_static);
+1 -3
View File
@@ -93,14 +93,12 @@ target_include_directories(mqtt_schema_test PRIVATE
)
target_link_libraries(mqtt_schema_test neuron-base gtest_main gtest)
file(COPY ${CMAKE_SOURCE_DIR}/tests/ut/EDE_test.csv DESTINATION ${UT_DIRECTORY}/config)
add_executable(ede_test ede_test.cc ${CMAKE_SOURCE_DIR}/src/utils/ede.c)
target_include_directories(ede_test PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
)
target_compile_definitions(ede_test PRIVATE
EDE_TEST_FILE_PATH="${CMAKE_SOURCE_DIR}/tests/EDE_test.csv"
)
target_link_libraries(ede_test neuron-base gtest_main gtest)
include(GoogleTest)
+1 -23
View File
@@ -14,6 +14,7 @@
zlog_category_t *neuron = NULL;
static constexpr size_t kExpectedEdeTagCount = 1499;
#define EDE_TEST_FILE_PATH "./config/EDE_test.csv"
static std::vector<std::string> split_semicolon(const std::string &line)
{
@@ -267,29 +268,6 @@ TEST(EdeTest, EdeToMsg)
free(cmd.groups);
}
TEST(EdeTest, CommandableToRw)
{
const char * tmp_file = "/tmp/ede_commandable_test.csv";
std::ofstream ofs(tmp_file);
ofs << "PROJECT_NAME;X\n";
ofs << "# keyname;device obj.-instance;object-name;object-type;"
"object-instance;description;present-value-default;"
"min-present-value;max-present-value;commandable\n";
ofs << "k1;2;obj;1;10;desc;;;;Y\n";
ofs << "k2;2;obj;1;11;desc;;;;N\n";
ofs.close();
neu_ede_result_t result = { 0 };
ASSERT_EQ(neu_ede_parse_file(tmp_file, &result), 0);
ASSERT_EQ(result.count, 2U);
EXPECT_EQ(result.entries[0].attribute,
(neu_attribute_e)(NEU_ATTRIBUTE_READ | NEU_ATTRIBUTE_WRITE));
EXPECT_EQ(result.entries[1].attribute, NEU_ATTRIBUTE_READ);
neu_ede_result_uninit(&result);
std::remove(tmp_file);
}
int main(int argc, char **argv)
{
zlog_init("./config/dev.conf");