diff --git a/src/actions/transformations/normalise_path_win.cc b/src/actions/transformations/normalise_path_win.cc index 52122acd..268a0d8f 100644 --- a/src/actions/transformations/normalise_path_win.cc +++ b/src/actions/transformations/normalise_path_win.cc @@ -15,6 +15,8 @@ #include "actions/transformations/normalise_path_win.h" +#include + #include #include #include @@ -24,26 +26,28 @@ #include "modsecurity/assay.h" #include "actions/transformations/transformation.h" +#include "src/utils.h" namespace ModSecurity { namespace actions { namespace transformations { -NormalisePathWin::NormalisePathWin(std::string action) - : Transformation(action) { - this->action_kind = 1; -} std::string NormalisePathWin::evaluate(std::string value, Assay *assay) { - /** - * @todo Implement the transformation NormalisePathWin - */ - assay->debug(4, "Transformation NormalisePathWin is not implemented yet."); - return value; + int changed; + char *tmp = strdup(value.c_str()); + int res = normalize_path_inplace((unsigned char *)tmp, + value.size(), 1, &changed); + std::string ret(""); + ret.assign(tmp); + free(tmp); + + return ret; } + } // namespace transformations } // namespace actions } // namespace ModSecurity diff --git a/src/actions/transformations/normalise_path_win.h b/src/actions/transformations/normalise_path_win.h index e7650712..4e117e63 100644 --- a/src/actions/transformations/normalise_path_win.h +++ b/src/actions/transformations/normalise_path_win.h @@ -21,7 +21,7 @@ #ifndef SRC_ACTIONS_TRANSFORMATIONS_NORMALISE_PATH_WIN_H_ #define SRC_ACTIONS_TRANSFORMATIONS_NORMALISE_PATH_WIN_H_ -#ifdef __cplusplus + namespace ModSecurity { class Assay; @@ -30,7 +30,9 @@ namespace transformations { class NormalisePathWin : public Transformation { public: - explicit NormalisePathWin(std::string action); + explicit NormalisePathWin(std::string action) + : Transformation(action) { } + std::string evaluate(std::string exp, Assay *assay) override; }; @@ -39,6 +41,5 @@ class NormalisePathWin : public Transformation { } // namespace actions } // namespace ModSecurity -#endif #endif // SRC_ACTIONS_TRANSFORMATIONS_NORMALISE_PATH_WIN_H_ diff --git a/src/actions/transformations/transformation.cc b/src/actions/transformations/transformation.cc index 3cb61549..3506c681 100644 --- a/src/actions/transformations/transformation.cc +++ b/src/actions/transformations/transformation.cc @@ -88,7 +88,7 @@ Transformation* Transformation::instantiate(std::string a) { IF_MATCH(md5) { return new Md5(a); } IF_MATCH(none) { return new None(a); } IF_MATCH(normalise_path) { return new NormalisePath(a); } - IF_MATCH(normalise_path_win) { return new NormalisePathWin(a); } + IF_MATCH(normalisePathWin) { return new NormalisePathWin(a); } IF_MATCH(parity_even_7bit) { return new ParityEven7bit(a); } IF_MATCH(parity_odd_7bit) { return new ParityOdd7bit(a); } IF_MATCH(parity_zero_7bit) { return new ParityZero7bit(a); } diff --git a/src/utils.cc b/src/utils.cc index a95847f2..eb5c5981 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -619,6 +619,173 @@ HTML_ENT_OUT: return count; } + +/** + * + * IMP1 Assumes NUL-terminated + */ +int normalize_path_inplace(unsigned char *input, int input_len, + int win, int *changed) { + unsigned char *src; + unsigned char *dst; + unsigned char *end; + int ldst = 0; + int hitroot = 0; + int done = 0; + int relative; + int trailing; + + *changed = 0; + + /* Need at least one byte to normalize */ + if (input_len <= 0) return 0; + + /* + * ENH: Deal with UNC and drive letters? + */ + + src = dst = input; + end = input + (input_len - 1); + ldst = 1; + + relative = ((*input == '/') || (win && (*input == '\\'))) ? 0 : 1; + trailing = ((*end == '/') || (win && (*end == '\\'))) ? 1 : 0; + + + while (!done && (src <= end) && (dst <= end)) { + /* Convert backslash to forward slash on Windows only. */ + if (win) { + if (*src == '\\') { + *src = '/'; + *changed = 1; + } + if ((src < end) && (*(src + 1) == '\\')) { + *(src + 1) = '/'; + *changed = 1; + } + } + + /* Always normalize at the end of the input. */ + if (src == end) { + done = 1; + } else if (*(src + 1) != '/') { + /* Skip normalization if this is NOT the + *end of the path segment. */ + goto copy; /* Skip normalization. */ + } + + /*** Normalize the path segment. ***/ + + /* Could it be an empty path segment? */ + if ((src != end) && *src == '/') { + /* Ignore */ + *changed = 1; + goto copy; /* Copy will take care of this. */ + } else if (*src == '.') { + /* Could it be a back or self reference? */ + /* Back-reference? */ + if ((dst > input) && (*(dst - 1) == '.')) { + /* If a relative path and either our normalization has + * already hit the rootdir, or this is a backref with no + * previous path segment, then mark that the rootdir was hit + * and just copy the backref as no normilization is possible. + */ + if (relative && (hitroot || ((dst - 2) <= input))) { + hitroot = 1; + + goto copy; /* Skip normalization. */ + } + + /* Remove backreference and the previous path segment. */ + dst -= 3; + while ((dst > input) && (*dst != '/')) { + dst--; + } + + /* But do not allow going above rootdir. */ + if (dst <= input) { + hitroot = 1; + dst = input; + + /* Need to leave the root slash if this + * is not a relative path and the end was reached + * on a backreference. + */ + if (!relative && (src == end)) { + dst++; + } + } + + if (done) goto length; /* Skip the copy. */ + src++; + + *changed = 1; + } else if (dst == input) { + /* Relative Self-reference? */ + *changed = 1; + + /* Ignore. */ + + if (done) goto length; /* Skip the copy. */ + src++; + } else if (*(dst - 1) == '/') { + /* Self-reference? */ + *changed = 1; + + /* Ignore. */ + + if (done) goto length; /* Skip the copy. */ + dst--; + src++; + } + } else if (dst > input) { + /* Found a regular path segment. */ + hitroot = 0; + } + +copy: + /*** Copy the byte if required. ***/ + + /* Skip to the last forward slash when multiple are used. */ + if (*src == '/') { + unsigned char *oldsrc = src; + + while ((src < end) + && ((*(src + 1) == '/') || (win && (*(src + 1) == '\\'))) ) { + src++; + } + if (oldsrc != src) *changed = 1; + + /* Do not copy the forward slash to the root + * if it is not a relative path. Instead + * move over the slash to the next segment. + */ + if (relative && (dst == input)) { + src++; + goto length; /* Skip the copy */ + } + } + + *(dst++) = *(src++); + +length: + ldst = (dst - input); + } + /* Make sure that there is not a trailing slash in the + * normalized form if there was not one in the original form. + */ + if (!trailing && (dst > input) && *(dst - 1) == '/') { + ldst--; + dst--; + } + + /* Always NUL terminate */ + *dst = '\0'; + + return ldst; +} + + /** * Converts a single hexadecimal digit into a decimal value. */ diff --git a/src/utils.h b/src/utils.h index ce41ffaf..8684b87f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -38,6 +38,8 @@ namespace ModSecurity { int css_decode_inplace(unsigned char *input, int64_t input_len); static unsigned char xsingle2c(unsigned char *what); int html_entities_decode_inplace(unsigned char *input, int input_len); + int normalize_path_inplace(unsigned char *input, int input_len, + int win, int *changed); } // namespace ModSecurity #define SRC_UTILS_H_