Skip to content

Commit

Permalink
http: stricter Transfer-Encoding and header separator parsing
Browse files Browse the repository at this point in the history
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com>
PR-URL: nodejs-private/node-private#315
CVE-ID: CVE-2022-32215,CVE-2022-32214,CVE-2022-32212

Backport-PR-URL: nodejs-private/node-private#326
  • Loading branch information
ShogunPanda authored and danielleadams committed Jul 6, 2022
1 parent c087644 commit 1da22eb
Show file tree
Hide file tree
Showing 11 changed files with 1,037 additions and 460 deletions.
116 changes: 89 additions & 27 deletions deps/llhttp/CMakeLists.txt
@@ -1,53 +1,115 @@
cmake_minimum_required(VERSION 3.5.1)
cmake_policy(SET CMP0069 NEW)

project(llhttp C)
project(llhttp VERSION 6.0.5)
include(GNUInstallDirs)

set(CMAKE_C_STANDARD 99)

# By default build in relwithdebinfo type, supports both lowercase and uppercase
if(NOT CMAKE_CONFIGURATION_TYPES)
set(allowableBuileTypes DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowableBuileTypes}")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RELWITHDEBINFO CACHE STRING "" FORCE)
else()
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
if(NOT CMAKE_BUILD_TYPE IN_LIST allowableBuileTypes)
message(FATEL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
endif()
endif()
endif()

#
# Options
#
# Generic option
option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF)
option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so)" ON)
option(BUILD_STATIC_LIBS "Build static libraries (.lib/.a)" OFF)

# Source code
set(LLHTTP_SOURCES
src/llhttp.c
src/http.c
src/api.c
${CMAKE_CURRENT_SOURCE_DIR}/src/llhttp.c
${CMAKE_CURRENT_SOURCE_DIR}/src/http.c
${CMAKE_CURRENT_SOURCE_DIR}/src/api.c
)

set(LLHTTP_HEADERS
include/llhttp.h
${CMAKE_CURRENT_SOURCE_DIR}/include/llhttp.h
)

add_library(llhttp)
add_library(llhttp::llhttp ALIAS llhttp)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/libllhttp.pc.in
${CMAKE_CURRENT_SOURCE_DIR}/libllhttp.pc
@ONLY
)

target_sources(llhttp PRIVATE ${LLHTTP_SOURCES} ${LLHTTP_HEADERS})
function(config_library target)
target_sources(${target} PRIVATE ${LLHTTP_SOURCES} ${LLHTTP_HEADERS})

# On windows with Visual Studio, add a debug postfix so that release
# and debug libraries can coexist.
if(MSVC)
set(CMAKE_DEBUG_POSTFIX "d")
endif()
target_include_directories(${target} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_include_directories(llhttp PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
set_target_properties(${target} PROPERTIES
OUTPUT_NAME llhttp
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
PUBLIC_HEADER ${LLHTTP_HEADERS}
)

set_target_properties(llhttp PROPERTIES PUBLIC_HEADER ${LLHTTP_HEADERS})
install(TARGETS ${target}
EXPORT llhttp
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(TARGETS llhttp
EXPORT llhttp
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include/
)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/libllhttp.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)

# This is required to work with FetchContent
install(EXPORT llhttp
# This is required to work with FetchContent
install(EXPORT llhttp
FILE llhttp-config.cmake
NAMESPACE llhttp::
DESTINATION lib/cmake/llhttp)
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/llhttp)
endfunction(config_library target)

if(BUILD_SHARED_LIBS)
add_library(llhttp_shared SHARED
${llhttp_src}
)
add_library(llhttp::llhttp ALIAS llhttp_shared)
config_library(llhttp_shared)
endif()

if(BUILD_STATIC_LIBS)
add_library(llhttp_static STATIC
${llhttp_src}
)
if(BUILD_SHARED_LIBS)
add_library(llhttp::llhttp ALIAS llhttp_shared)
else()
add_library(llhttp::llhttp ALIAS llhttp_static)
endif()
config_library(llhttp_static)
endif()

# On windows with Visual Studio, add a debug postfix so that release
# and debug libraries can coexist.
if(MSVC)
set(CMAKE_DEBUG_POSTFIX "d")
endif()

# Print project configure summary
message(STATUS "")
message(STATUS "")
message(STATUS "Project configure summary:")
message(STATUS "")
message(STATUS " CMake build type .................: ${CMAKE_BUILD_TYPE}")
message(STATUS " Install prefix ...................: ${CMAKE_INSTALL_PREFIX}")
message(STATUS " Build shared library .............: ${BUILD_SHARED_LIBS}")
message(STATUS " Build static library .............: ${BUILD_STATIC_LIBS}")
message(STATUS "")
36 changes: 33 additions & 3 deletions deps/llhttp/README.md
Expand Up @@ -90,7 +90,16 @@ if (err == HPE_OK) {
parser.reason);
}
```
For more information on API usage, please refer to [src/native/api.h](https://github.com/nodejs/llhttp/blob/master/src/native/api.h).
For more information on API usage, please refer to [src/native/api.h](https://github.com/nodejs/llhttp/blob/main/src/native/api.h).
## Build Instructions
Make sure you have [Node.js](https://nodejs.org/), npm and npx installed. Then under project directory run:
```sh
npm install
make
```

---

Expand All @@ -99,20 +108,41 @@ For more information on API usage, please refer to [src/native/api.h](https://gi
* Python: [pallas/pyllhttp][8]
* Ruby: [metabahn/llhttp][9]

### Using with CMake

If you want to use this library in a CMake project you can use the snippet below.

```
FetchContent_Declare(llhttp
URL "https://github.com/nodejs/llhttp/releases/download/v6.0.4/llhttp-release-v6.0.4.tar.gz") # Using version 6.0.4
URL "https://github.com/nodejs/llhttp/releases/download/v6.0.5/llhttp-release-v6.0.5.tar.gz") # Using version 6.0.5
FetchContent_MakeAvailable(llhttp)
target_link_libraries(${EXAMPLE_PROJECT_NAME} ${PROJECT_LIBRARIES} llhttp ${PROJECT_NAME})
```

## Building on Windows

### Installation

* `choco install git`
* `choco install node`
* `choco install llvm` (or install the `C++ Clang tools for Windows` optional package from the Visual Studio 2019 installer)
* `choco install make` (or if you have MinGW, it comes bundled)

1. Ensure that `Clang` and `make` are in your system path.
2. Using Git Bash, clone the repo to your preferred location.
3. Cd into the cloned directory and run `npm install`
5. Run `make`
6. Your `repo/build` directory should now have `libllhttp.a` and `libllhttp.so` static and dynamic libraries.
7. When building your executable, you can link to these libraries. Make sure to set the build folder as an include path when building so you can reference the declarations in `repo/build/llhttp.h`.

### A simple example on linking with the library:

Assuming you have an executable `main.cpp` in your current working directory, you would run: `clang++ -Os -g3 -Wall -Wextra -Wno-unused-parameter -I/path/to/llhttp/build main.cpp /path/to/llhttp/build/libllhttp.a -o main.exe`.

If you are getting `unresolved external symbol` linker errors you are likely attempting to build `llhttp.c` without linking it with object files from `api.c` and `http.c`.

#### LICENSE

This software is licensed under the MIT License.
Expand Down
24 changes: 18 additions & 6 deletions deps/llhttp/include/llhttp.h
Expand Up @@ -3,7 +3,7 @@

#define LLHTTP_VERSION_MAJOR 6
#define LLHTTP_VERSION_MINOR 0
#define LLHTTP_VERSION_PATCH 4
#define LLHTTP_VERSION_PATCH 7

#ifndef LLHTTP_STRICT_MODE
# define LLHTTP_STRICT_MODE 0
Expand Down Expand Up @@ -59,6 +59,7 @@ enum llhttp_errno {
HPE_OK = 0,
HPE_INTERNAL = 1,
HPE_STRICT = 2,
HPE_CR_EXPECTED = 25,
HPE_LF_EXPECTED = 3,
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
HPE_CLOSED_CONNECTION = 5,
Expand Down Expand Up @@ -100,7 +101,8 @@ typedef enum llhttp_flags llhttp_flags_t;
enum llhttp_lenient_flags {
LENIENT_HEADERS = 0x1,
LENIENT_CHUNKED_LENGTH = 0x2,
LENIENT_KEEP_ALIVE = 0x4
LENIENT_KEEP_ALIVE = 0x4,
LENIENT_TRANSFER_ENCODING = 0x8
};
typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;

Expand Down Expand Up @@ -172,6 +174,7 @@ typedef enum llhttp_method llhttp_method_t;
XX(0, OK, OK) \
XX(1, INTERNAL, INTERNAL) \
XX(2, STRICT, STRICT) \
XX(25, CR_EXPECTED, CR_EXPECTED) \
XX(3, LF_EXPECTED, LF_EXPECTED) \
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
Expand Down Expand Up @@ -374,8 +377,6 @@ LLHTTP_EXPORT
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings);

#if defined(__wasm__)

LLHTTP_EXPORT
llhttp_t* llhttp_alloc(llhttp_type_t type);

Expand All @@ -400,8 +401,6 @@ int llhttp_get_status_code(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_upgrade(llhttp_t* parser);

#endif // defined(__wasm__)

/* Reset an already initialized parser back to the start state, preserving the
* existing parser type, callback settings, user data, and lenient flags.
*/
Expand Down Expand Up @@ -556,6 +555,19 @@ void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
*/
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);

/* Enables/disables lenient handling of `Transfer-Encoding` header.
*
* Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
* and another value after it (either in a single header or in multiple
* headers whose value are internally joined using `, `).
* This is mandated by the spec to reliably determine request body size and thus
* avoid request smuggling.
* With this flag the extra value will be parsed normally.
*
* **(USE AT YOUR OWN RISK)**
*/
void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);

#ifdef __cplusplus
} /* extern "C" */
#endif
Expand Down
10 changes: 10 additions & 0 deletions deps/llhttp/libllhttp.pc.in
@@ -0,0 +1,10 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@
includedir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@

Name: libllhttp
Description: Node.js llhttp Library
Version: @PROJECT_VERSION@
Libs: -L${libdir} -lllhttp
Cflags: -I${includedir}
8 changes: 8 additions & 0 deletions deps/llhttp/src/api.c
Expand Up @@ -253,6 +253,14 @@ void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
}
}

void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
} else {
parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
}
}

/* Callbacks */


Expand Down
3 changes: 2 additions & 1 deletion deps/llhttp/src/http.c
Expand Up @@ -52,7 +52,8 @@ int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
return 2;
} else if (parser->flags & F_TRANSFER_ENCODING) {
if (parser->type == HTTP_REQUEST &&
(parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0) {
(parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&
(parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) {
/* RFC 7230 3.3.3 */

/* If a Transfer-Encoding header field
Expand Down

0 comments on commit 1da22eb

Please sign in to comment.