Bugfix/validate-user-provided-file-object-paths (#1197)

* Fixed BACnet file object path name unintended path traversals by optionally restricting path name content with BACNET_FILE_PATH_RESTRICTED define.

* Added POSIX file path name checking for AtomicReadFile and AtomicWriteFile example applications. Prohibits use of relative and absolute file paths when BACNET_FILE_PATH_RESTRICTED is non-zero.
This commit is contained in:
Steve Karg
2026-01-05 11:19:52 -06:00
committed by GitHub
parent 715e45eb5c
commit c5dc00a77b
11 changed files with 151 additions and 16 deletions
-3
View File
@@ -29,9 +29,6 @@
#include "bacnet/basic/sys/keylist.h"
#include "bacnet/basic/tsm/tsm.h"
#ifndef FILE_RECORD_SIZE
#define FILE_RECORD_SIZE MAX_OCTET_STRING_BYTES
#endif
struct object_data {
char *Object_Name;
char *Pathname;
+66 -1
View File
@@ -1,14 +1,25 @@
/**
* @file
* @brief Function for filename manipulation
* @brief Function for filename and path manipulation and validation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2007
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdio.h>
#include <string.h>
#include "bacnet/basic/sys/debug.h"
#include "bacnet/basic/sys/filename.h"
/* restrict file paths */
#ifndef BACNET_FILE_PATH_RESTRICTED
#define BACNET_FILE_PATH_RESTRICTED 1
#endif
/**
* @brief Remove path from filename
* @param filename_in - input filename with path
* @return filename without path
*/
const char *filename_remove_path(const char *filename_in)
{
const char *filename_out = filename_in;
@@ -30,3 +41,57 @@ const char *filename_remove_path(const char *filename_in)
return filename_out;
}
/**
* @brief Validate if pathname is valid by checking for patterns
* such as relative paths and absolute paths which are prohibited.
* @param pathname Path to validate
* @return true if valid, false if not
*/
bool filename_path_valid(const char *pathname)
{
int path_len;
if (!pathname) {
return false;
}
if (pathname[0] == 0) {
return false;
}
#if BACNET_FILE_PATH_RESTRICTED
/* check for relative directory patterns */
if (strstr(pathname, "..") != NULL) {
debug_printf_stderr("Relative paths are prohibited: %s\n", pathname);
return false;
}
/* check for absolute paths */
if (pathname[0] == '/') {
debug_printf_stderr("Absolute paths are prohibited: %s\n", pathname);
return false;
}
/* check for Windows drive letters (should be relative paths only) */
path_len = strlen(pathname);
if (path_len >= 2 && pathname[1] == ':') {
debug_printf_stderr(
"Windows drive letters are prohibited: %s\n", pathname);
return false;
}
/* check for consecutive path separators */
if (strstr(pathname, "//") != NULL || strstr(pathname, "\\\\") != NULL) {
debug_printf_stderr(
"Consecutive path separators are prohibited: %s\n", pathname);
return false;
}
/* check for path components that are just dots */
if (strstr(pathname, "/./") != NULL || strstr(pathname, "\\./") != NULL ||
strstr(pathname, "/.\\") != NULL || strstr(pathname, "\\.\\") != NULL) {
debug_printf_stderr(
"Current directory references are prohibited: %s\n", pathname);
return false;
}
#endif
return true;
}
+6
View File
@@ -9,6 +9,10 @@
#define BACNET_SYS_FILENAME_H
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* standard includes */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
@@ -16,6 +20,8 @@ extern "C" {
BACNET_STACK_EXPORT
const char *filename_remove_path(const char *filename_in);
BACNET_STACK_EXPORT
bool filename_path_valid(const char *pathname);
#ifdef __cplusplus
}