Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ set(NEURON_BASE_SOURCES
src/utils/log.c
src/utils/cid.c
src/utils/tpy.c
src/utils/neu_path.c
${PERSIST_SOURCES})

if (SMART_LINK)
Expand Down
1 change: 1 addition & 0 deletions include/neuron/neuron.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
50 changes: 50 additions & 0 deletions include/neuron/utils/neu_path.h
Original file line number Diff line number Diff line change
@@ -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
18 changes: 12 additions & 6 deletions plugins/file/file_plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -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(
Expand Down
22 changes: 18 additions & 4 deletions plugins/monitor/mqtt_handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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: {
Expand Down
38 changes: 35 additions & 3 deletions plugins/mqtt/mqtt_handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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++) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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]);
Expand All @@ -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;
}

Expand Down
4 changes: 3 additions & 1 deletion plugins/restful/adapter_handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
24 changes: 22 additions & 2 deletions src/base/group.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
7 changes: 4 additions & 3 deletions src/base/metrics.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 6 additions & 3 deletions src/base/tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions src/connection/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions src/core/manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading