Search code examples
macosg++static-librariesstatic-linkingsetuid

Compiling a binary immune to library redirection on Mac OS X


I notice that on Mac OS X (currently I am running Yosemite, but I had the same problem on earlier versions of OS X) whenever I invoke g++ (or even gcc for that matter) with the -static option, the linking of the binary fails -- always due to some library or another not being successfully found. As a result, I am only able to produce binaries that use shared libraries.

This is a problem, as I have a situation that I can not resolve without writing a program that gets installed with Setuid Root permission. This program I need to write will inspect the environment variables to assure that they are free of any malice (and it will do so with a better-safe-than-sorry attitude) and then it will demote it's user-ID to that of a regular user before using a form of exec() to invoke another program.

These security checks of the environment will assure that the other program can run safely under the assurance that critical environment variables (such as DYLD_LIBRARY_PATH) are not corrupted. But this program itself can not run under any such assurance - because it is part of it's job to perform this check in the first place.

So how can I protect this C++ program from such a security vulnerability? My first thought was to compile it with the -static option --- but since this does not work on OS X, does anyone have any other ideas?

Thanks.


Solution

  • EDIT: As long as the executable is setuid, the loader kills all DYLD_* environment variables. From the source code for dyld.cpp:

    // For security, setuid programs ignore DYLD_* environment variables.
    // Additionally, the DYLD_* enviroment variables are removed
    // from the environment, so that any child processes don't see them.
    

    [original answer:] According to this post by Sam Marshall, you can do it by adding new section to the binary header named "__RESTRICT", with a section named "__restrict". You can do this in Xcode by adding this to to "Other Linker Flags":

    -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
    

    The other possible ways to get DYLD_LIBRARY_PATH ignored are to have the executable be setuid or setgid, or signed with entitlements. From the source code for dyld.cpp:

    dyld::log("dyld: DYLD_ environment variables being ignored because ");
    switch (sRestrictedReason) {
        case restrictedNot:
            break;
        case restrictedBySetGUid:
            dyld::log("main executable (%s) is setuid or setgid\n", sExecPath);
            break;
        case restrictedBySegment:
            dyld::log("main executable (%s) has __RESTRICT/__restrict section\n", sExecPath);
            break;
        case restrictedByEntitlements:
            dyld::log("main executable (%s) is code signed with entitlements\n", sExecPath);
            break;
    }