c++cgccinclude-pathjail

How do I prevent an include of files out of system directories?


I have to prevent the #include of any files out of system directories because of security reasons. Is there any restrictions that can prevent #include<...> and #include"..."from including unsafe files like #include </dev/tty> or #include "/dev/random"?

I have read the header files chapter of Docs of C Preprocessor as well as a similar question How do I prevent a quoted include from searching the directory of the current source file? but can't find a proper way to do so.

Since -I- command is obsolete and there is no other way that can accomplish the same function, can I use jailkit to compile the code as a low-privilege user? Or is there other way to ensure the security of compile process?


Solution

  • godbolt.org solves a similar problem by forbidding absolute and relative paths in #includes:

    Input:

    #include "/etc/passwd"
    

    Output:

    <stdin>:1:1: no absolute or relative includes please
    Compilation failed
    

    This can be achieved by running a simple checker on the sources, before invoking the compiler. The checker only has to grep for #include directives and check the paths against violations of such restrictions. A draft version of such a script (usable with a single source file) follows:

    check_includes:

    #!/bin/bash
    
    if (( $# != 1 ))
    then
        echo "Usage: $(basename "$0") source_file"
        exit 1
    fi
    
    join_lines()
    {
        sed '/\\$/ {N;s/\\\n//;s/^/\n/;D}' "$1"
    }
    
    absolute_includes()
    {
        join_lines "$1"|grep '^\s*#\s*include\s*["<]\s*/'
    }
    
    relative_includes()
    {
        join_lines "$1"|grep '^\s*#\s*include'|fgrep '../'
    }
    
    includes_via_defines()
    {
        join_lines "$1"|grep '^\s*#\s*include\s*[^"< \t]'
    }
    
    show_and_count()
    {
        tee /dev/stderr|wc -l
    }
    
    exit_status=0
    if (( 0 != $(absolute_includes "$1"|show_and_count) ))
    then
        echo 1>&2 "ERROR: $1 contains absolute includes"
        exit_status=1
    fi
    if (( 0 != $(relative_includes "$1"|show_and_count) ))
    then
        echo 1>&2 "ERROR: $1 contains relative includes"
        exit_status=1
    fi
    if (( 0 != $(includes_via_defines "$1"|show_and_count) ))
    then
        echo 1>&2 "ERROR: $1 contains includes via defines"
        exit_status=1
    fi
    exit $exit_status
    

    Assuming that the input file can be compiled without errors, this script identifies #includes that contain absolute or relative paths. It also detects includes done via a macro like below (this can be abused to work around the path checking):

    #define HEADER "/etc/password"
    #include HEADER