Example #1
0
DWORD
DJIsAppleADPluginInUse(BOOLEAN* pExists)
{
    DWORD ceError = ERROR_SUCCESS;
    PSTR value = NULL;
    PSTR valueStart = NULL;
    BOOLEAN bInUse = FALSE;

    DJ_LOG_INFO("Testing to see if Apple AD plugin is already in use");

    ceError = CTCaptureOutput("/usr/bin/dscl localhost -list / | grep \"^Active Directory\"", &value);
    if (ceError)
    {
        ceError = ERROR_SUCCESS;
        goto error;
    }

    CTStripWhitespace(value);
    valueStart = strstr(value, "Active Directory");
    if (valueStart != NULL)
    {
        bInUse = TRUE;
    }

error:

    CT_SAFE_FREE_STRING(value);

    *pExists = bInUse;

    return ceError;
}
Example #2
0
static void GetSshVersion(PCSTR rootPrefix, SshdVersion *version, PCSTR binaryPath, LWException **exc)
{
    DWORD ceError = ERROR_SUCCESS;
    PSTR command = NULL;
    PSTR commandOutput = NULL;
    PCSTR versionStart;
    PSTR intEnd;

    version->isOpenSsh = FALSE;
    version->major = -1;
    version->minor = -1;
    version->secondMinor = -1;
    version->patch = -1;

    if(rootPrefix == NULL)
        rootPrefix = "";

    LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(
        &command, "%s%s -v 2>&1",
        rootPrefix, binaryPath));

    ceError = CTCaptureOutput(command, &commandOutput);
    if(ceError == ERROR_BAD_COMMAND)
        ceError = ERROR_SUCCESS;
    LW_CLEANUP_CTERR(exc, ceError);

    // The version string is in the form OpenSSH_4.6p1
    // or OpenSSH_3.6.1p1
    versionStart = strstr(commandOutput, "OpenSSH_");
    if(versionStart == NULL)
    {
        goto cleanup;
    }
    version->isOpenSsh = TRUE;

    versionStart += strlen("OpenSSH_");
    version->major = strtoul(versionStart, &intEnd, 10);
    if(intEnd == versionStart)
        version->major = -1;
    if(intEnd == NULL || *intEnd != '.')
        goto cleanup;
    versionStart = intEnd + 1;

    version->minor = strtoul(versionStart, &intEnd, 10);
    if(intEnd == versionStart)
        version->minor = -1;
    if(intEnd == NULL || (*intEnd != '.' && *intEnd != 'p'))
        goto cleanup;
    versionStart = intEnd + 1;

    if(*intEnd == '.')
    {
        version->secondMinor = strtoul(versionStart, &intEnd, 10);
        if(intEnd == versionStart)
            version->secondMinor = -1;
        if(intEnd == NULL || *intEnd != 'p')
            goto cleanup;
        versionStart = intEnd + 1;
    }

    version->patch = strtoul(versionStart, &intEnd, 10);
    if(intEnd == versionStart)
        version->patch = -1;

cleanup:

    if(version->isOpenSsh)
    {
        DJ_LOG_INFO("Found open sshd version %d.%d.%dp%d", version->major,
                version->minor, version->secondMinor, version->patch);
    }
    else
    {
        DJ_LOG_INFO("Found non-openssh version of ssh");
    }

    CT_SAFE_FREE_STRING(command);
    CT_SAFE_FREE_STRING(commandOutput);
}
Example #3
0
static BOOLEAN TestOption(PCSTR rootPrefix, struct SshConf *conf, PCSTR binary, PCSTR testFlag, PCSTR optionName, LWException **exc)
{
    DWORD ceError = ERROR_SUCCESS;
    BOOLEAN result = FALSE;
    PSTR command = NULL;
    PSTR commandOutput = NULL;
    int existingLine;

    if(rootPrefix == NULL)
        rootPrefix = "";
    DJ_LOG_INFO("Testing option %s", optionName);

    existingLine = FindOption(conf, 0, optionName);
    if(existingLine != -1)
    {
        if(!strcmp(conf->lines[existingLine].value.value, "yes"))
        {
            //The option is already enabled, so it must be supported
            result = TRUE;
            goto cleanup;
        }
    }

    /* Most versions of sshd support the -t option which runs it in test
       mode. Test mode is used to verify that a config file is correct, or
       in our case that the passed options are valid.

       The only version of sshd known to not support -t is the version that
       comes with Solaris 9. However, this version does not support the -o
       option, and it will error out if the -o option is used. The Solaris
       9 version of sshd does not support any of the options we'd like to
       enable, so it will correctly fail all of the option tests.

       Sshd will either complain about the first invalid option that is
       passed with -o, or it will complain about all invalid options. -o
       BadOption=yes is passed to verify that sshd understands -o, and to
       make doubly sure that it will not start listening on a port.

       The -v option can be used to test whether ssh (the client) supports
       given options.  Passing -v to ssh will cause it to print out its
       version number and not attempt to connect to a machine. All versions of
       ssh seem to parse the options passed with -o even when -v is passed.

       Ssh will either complain about the first invalid option that is
       passed with -o, or it will complain about all invalid options. -o
       BadOption=yes is passed to verify that ssh understands -o.
     */
    LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(
        &command, "%s%s %s -o %s=yes -o BadOption=yes 2>&1",
        rootPrefix, binary, testFlag, optionName));

    ceError = CTCaptureOutput(command, &commandOutput);
    /* Some versions of sshd will return an error code because an invalid
       option was passed, but not all will. */
    if(ceError == ERROR_BAD_COMMAND)
        ceError = ERROR_SUCCESS;
    LW_CLEANUP_CTERR(exc, ceError);

    if(strstr(commandOutput, optionName) != NULL)
    {
        DJ_LOG_INFO("Option %s not supported", optionName);
        goto cleanup;
    }

    if(strstr(commandOutput, "BadOption") == NULL)
    {
        DJ_LOG_INFO("Sshd does not support -o");
        goto cleanup;
    }

    DJ_LOG_INFO("Option %s supported", optionName);
    result = TRUE;

cleanup:
    CT_SAFE_FREE_STRING(command);
    CT_SAFE_FREE_STRING(commandOutput);
    return result;
}
Example #4
0
static DWORD UnsupportedSeLinuxEnabled(BOOLEAN *hasBadSeLinux)
{
    BOOLEAN hasSeLinux;
    DWORD ceError = ERROR_SUCCESS;
    PSTR output = NULL;
    DistroInfo distro;

    *hasBadSeLinux = FALSE;
    memset(&distro, 0, sizeof(distro));

    GCE(ceError = CTCheckFileOrLinkExists("/usr/sbin/selinuxenabled", &hasSeLinux));
    if(!hasSeLinux)
        goto cleanup;

    GCE(ceError = CTCheckFileOrLinkExists("/usr/sbin/getenforce", &hasSeLinux));
    if(!hasSeLinux)
        goto cleanup;

    ceError = CTRunCommand("/usr/sbin/selinuxenabled >/dev/null 2>&1");
    if(ceError == ERROR_BAD_COMMAND)
    {
        //selinux is not enabled
        ceError = ERROR_SUCCESS;
        goto cleanup;
    }
    GCE(ceError);

    GCE(ceError = CTCaptureOutput("/usr/sbin/getenforce", &output));
    CTStripWhitespace(output);
    if(!strcmp(output, "Permissive"))
    {
        goto cleanup;
    }

    DJ_LOG_INFO("Selinux found to be present, enabled, and enforcing.");

    GCE(ceError = DJGetDistroInfo("", &distro));

    switch(distro.distro)
    {
        case DISTRO_CENTOS:
        case DISTRO_RHEL:
            if(distro.version[0] < '5')
            {
                DJ_LOG_INFO("Safe version of RHEL");
                goto cleanup;
            }
            break;
        case DISTRO_FEDORA:
            if(atol(distro.version) < 6)
            {
                DJ_LOG_INFO("Safe version of Fedora");
                goto cleanup;
            }
            break;
        default:
            goto cleanup;
    }
    *hasBadSeLinux = TRUE;

cleanup:
    if(ceError)
        *hasBadSeLinux = TRUE;

    CT_SAFE_FREE_STRING(output);
    DJFreeDistroInfo(&distro);

    return ceError;
}
Example #5
0
static QueryResult QueryDSPlugin(const JoinProcessOptions *options, LWException **exc)
{
    BOOLEAN exists = FALSE;
    QueryResult result = NotConfigured;
    PSTR value = NULL;
    PSTR valueStart = NULL;
    BOOLEAN bLikewisePresent = FALSE;

    if (options->enableMultipleJoins)
    {
        result = NotApplicable;
        goto cleanup;
    }

    LW_CLEANUP_CTERR(exc, CTCheckFileOrLinkExists("/usr/bin/dscl", &exists));
    if(!exists)
    {
        result = NotApplicable;
        goto cleanup;
    }

    LW_CLEANUP_CTERR(exc, DJIsAppleADPluginInUse(&exists));
    if(exists)
    {
        result = ApplePluginInUse;
        goto cleanup;
    }

    LW_CLEANUP_CTERR(exc, CTCaptureOutput("/usr/bin/dscl /Search -read / dsAttrTypeStandard:SearchPolicy", &value));
    CTStripWhitespace(value);
    valueStart = value;
    if(CTStrStartsWith(valueStart, "SearchPolicy:"))
        valueStart += strlen("SearchPolicy:");
    CTStripWhitespace(valueStart);
    if(CTStrStartsWith(valueStart, "dsAttrTypeStandard:"))
      valueStart += strlen("dsAttrTypeStandard:");
    CTStripWhitespace(valueStart);
    if(options->joiningDomain)
    {
        if(strcmp(valueStart, "CSPSearchPath"))
            goto cleanup;
    }
    else
    {
        if(strcmp(valueStart, "NSPSearchPath"))
            goto cleanup;
    }
    CT_SAFE_FREE_STRING(value);

    LW_CLEANUP_CTERR(exc, CTCaptureOutput("/usr/bin/dscl /Search -read / dsAttrTypeStandard:CSPSearchPath", &value));
    CTStripWhitespace(value);
    valueStart = strstr(value, LWDSPLUGIN_NAME);
    if (valueStart == NULL)
    {
        bLikewisePresent = FALSE;
    }
    else
    {
        switch (valueStart[strlen(LWDSPLUGIN_NAME)])
        {
            case 0:
            case '\r':
            case '\n':
                bLikewisePresent = TRUE;
                break;
            default:
                bLikewisePresent = FALSE;
        }
    }

    if( (bLikewisePresent != 0) != (options->joiningDomain != 0) )
        goto cleanup;
    CT_SAFE_FREE_STRING(value);

    result = FullyConfigured;

cleanup:
    CT_SAFE_FREE_STRING(value);
    return result;
}
Example #6
0
DWORD CTGetDistroInfo(const char *testPrefix, CtDistroInfo *info)
{
    BOOLEAN exists;
    DWORD ceError = ERROR_SUCCESS;
    struct utsname unameStruct;
    char *path = NULL;
    PSTR fileContents = NULL;
    PSTR distroString = NULL;
    BOOLEAN rxAlloced = FALSE;
    regex_t rx;
    memset(info, 0, sizeof(*info));
#if defined(HAVE_SYSINFO) && defined(SI_ARCHITECTURE)
    char archBuffer[100];
#endif

    //According to the Solaris man page, any non-negative return value
    //indicates success. In fact, Solaris 8 returns 1, while Linux returns
    //0.
    if(uname(&unameStruct) < 0)
        return LwMapErrnoToLwError(errno);

    //Check for os override file
    if(testPrefix == NULL)
        testPrefix = "";
    GCE(ceError = CTAllocateStringPrintf(
            &path, "%s/ostype", testPrefix));
    GCE(ceError = CTCheckFileOrLinkExists(
            path, &exists));
    if(exists)
    {
        GCE(ceError = CTReadFile(
                path, SIZE_MAX, &fileContents, NULL));
    }
    if(fileContents != NULL)
    {
        CTStripWhitespace(fileContents);
        info->os = CTGetOSFromString(fileContents);
    }
    else
        info->os = CTGetOSFromString(unameStruct.sysname);
    CT_SAFE_FREE_STRING(fileContents);
    CT_SAFE_FREE_STRING(path);

    //Check for distro override file
    GCE(ceError = CTAllocateStringPrintf(
            &path, "%s/osdistro", testPrefix));
    GCE(ceError = CTCheckFileOrLinkExists(
            path, &exists));
    if(exists)
    {
        GCE(ceError = CTReadFile(
                path, SIZE_MAX, &distroString, NULL));
    }
    CT_SAFE_FREE_STRING(path);

    if(distroString != NULL)
    {
        CTStripWhitespace(distroString);
        info->distro = CTGetDistroFromString(distroString);
        CT_SAFE_FREE_STRING(distroString);
        GCE(ceError = CTStrdup("unknown", &info->version));
    }
    else if(info->os == OS_LINUX)
    {
        struct
        {
            CtDistroType matchDistro;
            const char *searchFile;
            const char *matchRegex;
            int versionMatchNum;
            int updateMatchNum;
            BOOLEAN compareCase;
        } const distroSearch[] = {
            {
                DISTRO_ESX,
                "/etc/vmware-release",
                /*
                # The format of the line is something like:
                #   VMware ESX 4.0 (Kandinsky)
                */
                "^[[:space:]]*VMware ESX ([[:digit:]]+(\\.[[:digit:]]+)?)"
                "( \\(\\S+\\))?",
                1,
                -1,
                1
            },
            {
                DISTRO_RHEL,
                "/etc/redhat-release",
                /*
                # The format of the line is something like:
                #   Red Hat Enterprise Linux ES release 4 (Nahant Update 1)
                #   Red Hat Linux Advanced Server release 2.1AS (Pensacola)
                #   Red Hat Enterprise Linux Client release 5 (Tikanga)
                #   Red Hat Enterprise Linux Server release 5.3 (Tikanga)
                #   Red Hat Enterprise Linux ES release 4 (Nahant Update 8)
                #   Red Hat Enterprise Linux Workstation release 6.0 (Santiago)
                # In addition, Oracle Linux reports itself as:
                #   Enterprise Linux Enterprise Linux AS release 4 (October Update 5)
                */
                //Find a matching distro name
                "^[[:space:]]*((Red Hat)|(Enterprise Linux)) ((Enterprise Linux)|(Linux (Advanced|Enterprise) Server))[[:space:]]+(AS |ES |Client |Server |Workstation )?"
                "release ([[:digit:]]+(\\.[[:digit:]]+)?(AS|ES)?) (\\(\\S+ Update ([[:digit:]]+)\\))?",
                9,
                13,
                1
            },
            {
                DISTRO_REDHAT,
                "/etc/redhat-release",
                /*
                # The format of the line is something like:
                #   Red Hat Linux release 7.3 (Valhala)
                */
                "^[[:space:]]*Red Hat Linux release ([[:digit:]]+(\\.[[:digit:]]+)?)",
                1,
                -1,
                1
            },
            {
                DISTRO_FEDORA,
                "/etc/redhat-release",
                /*
                # The format of the line is something like:
                #   Fedora Core release 4 (Stentz)
                */
                "^[[:space:]]*Fedora (Core )?release (\\S+)",
                2,
                -1,
                1
            },
            {
                DISTRO_CENTOS,
                "/etc/redhat-release",
                /*
                # The format of the line is something like:
                #   CentOS release 4.x (Final)
                */
                "^[[:space:]]*(CentOS|CentOS Linux) release ([[:digit:]]+(\\.[[:digit:]]+)?)",
                2,
                -1,
                1
            },
            {
                DISTRO_SCIENTIFIC,
                "/etc/redhat-release",
                /*
                # The format of the line is something like:
                #   Scientific Linux release 6.1 (Carbon)
                */
                "^[[:space:]]*(Scientific|Scientific Linux) release ([[:digit:]]+(\\.[[:digit:]]+)?)",
                2,
                -1,
                1
            },
            {
                DISTRO_SUSE,
                "/etc/SuSE-release",
                "^[[:space:]]*SUSE LINUX ([[:digit:]]+\\.[[:digit:]]+)[[:space:]]+",
                1,
                -1,
                0
            },
            {
                DISTRO_OPENSUSE,
                "/etc/SuSE-release",
                "^[[:space:]]*openSUSE ([[:digit:]]+\\.[[:digit:]]+)[[:space:]]+",
                1,
                -1,
                0
            },
            {
                DISTRO_SLES,
                "/etc/SuSE-release",
                "^[[:space:]]*SUSE LINUX Enterprise Server ([[:digit:]]+( SP[[:digit:]]+)?)",
                1,
                -1,
                0
            },
            {
                DISTRO_SLED,
                "/etc/SuSE-release",
                "^[[:space:]]*SUSE LINUX Enterprise Desktop ([[:digit:]]+( SP[[:digit:]]+)?)",
                1,
                -1,
                0
            },
            {
                DISTRO_UBUNTU,
                "/etc/lsb-release",
                /*
                # The file will have lines that include:
                #   DISTRIB_ID=Ubuntu
                #   DISTRIB_RELEASE=6.06
                */
                "^[[:space:]]*DISTRIB_ID[[:space:]]*=[[:space:]]*Ubuntu[[:space:]]*\n"
                "(.*\n)?DISTRIB_RELEASE[[:space:]]*=[[:space:]]*(\\S+)[[:space:]]*(\n.*)?$",
                2,
                -1,
                1
            },
            {
                DISTRO_DEBIAN,
                "/etc/debian_version",
                /*
                # The format of the entire file is a single line like:
                # 3.1
                # and nothing else, so that is the version
                */
                "^[[:space:]]*(\\S+)[[:space:]]*$",
                1,
                -1,
                1
            },
        };
        int i;
        regmatch_t matches[20];
        info->distro = DISTRO_UNKNOWN;
        for(i = 0; info->distro == DISTRO_UNKNOWN; i++)
        {
            if(i == sizeof(distroSearch)/sizeof(distroSearch[0]))
            {
                //We past the last item in DistroSearch
                break;
            }
            GCE(ceError = CTAllocateStringPrintf(
                    &path, "%s%s", testPrefix, distroSearch[i].searchFile));
            GCE(ceError = CTCheckFileOrLinkExists(path, &exists));
            if(exists)
            {
                int flags = REG_EXTENDED;
                if(!distroSearch[i].compareCase)
                    flags |= REG_ICASE;

                GCE(ceError = CTReadFile(path, SIZE_MAX, &fileContents, NULL));
                if(regcomp(&rx, distroSearch[i].matchRegex, flags) != 0)
                {
                    GCE(ceError = LW_ERROR_REGEX_COMPILE_FAILED);
                }
                rxAlloced = TRUE;
                if(regexec(&rx, fileContents,
                        sizeof(matches)/sizeof(matches[0]), matches, 0) == 0 &&
                        matches[distroSearch[i].versionMatchNum].rm_so != -1)
                {
                    //This is the correct distro
                    regmatch_t *ver = &matches[distroSearch[i].versionMatchNum];
                    regmatch_t *update = &matches[distroSearch[i].updateMatchNum];
                    info->distro = distroSearch[i].matchDistro;
                    if (distroSearch[i].updateMatchNum == -1 ||
                            update->rm_so == -1)
                    {
                        GCE(ceError = CTStrndup(fileContents + ver->rm_so,
                                    ver->rm_eo - ver->rm_so,
                                    &info->version));
                    }
                    else
                    {
                        GCE(ceError = CTAllocateStringPrintf(
                                &info->version,
                                "%.*s.%.*s",
                                ver->rm_eo - ver->rm_so,
                                fileContents + ver->rm_so,
                                update->rm_eo - update->rm_so,
                                fileContents + update->rm_so));
                    }
                }
                regfree(&rx);
                rxAlloced = FALSE;
                CT_SAFE_FREE_STRING(fileContents);
            }
            CT_SAFE_FREE_STRING(path);
        }
        if(info->distro == DISTRO_DEBIAN)
        {
            /*
            #
            # Debian and Ubuntu both have /etc/debian_version,
            # but only Ubuntu has an /etc/lsb-release
            #
            */
            GCE(ceError = CTAllocateStringPrintf(
                    &path, "%s/etc/lsb-release", testPrefix));
            GCE(ceError = CTCheckFileOrLinkExists(path, &exists));
            if(exists)
            {
                // CT_LOG_ERROR("Unexpected file: %s", path);
                info->distro = DISTRO_UNKNOWN;
            }
            CT_SAFE_FREE_STRING(path);
        }
    }
    else
    {
        //It's a UNIX system
        switch(info->os)
        {
        case OS_AIX:
            info->distro = DISTRO_AIX;
            /*Uname output from AIX 5.3:
            $ uname -v
            5
            $ uname -r
            3
            */
            GCE(ceError = CTAllocateStringPrintf(&info->version,
                        "%s.%s", unameStruct.version, unameStruct.release));
            break;
        case OS_SUNOS:
            info->distro = DISTRO_SUNOS;
            /*Uname output from Solaris 8:
            $ uname -r
            5.8
            */
            GCE(ceError = CTAllocateStringPrintf(&info->version,
                        "%s", unameStruct.release));
            break;
        case OS_DARWIN:
            info->distro = DISTRO_DARWIN;
            ceError = CTGetPListVersion(&info->version);
            GCE(ceError);
            CTStripWhitespace(info->version);
            break;
        case OS_HPUX:
            info->distro = DISTRO_HPUX;
            {
                const char *temp = unameStruct.release;
                while(!isdigit((int)*temp)) temp++;
                GCE(ceError = CTStrdup(temp, &info->version));
            }
            break;
        case OS_FREEBSD:
            info->distro = DISTRO_FREEBSD;
            GCE(ceError = CTAllocateStringPrintf(&info->version,
                        "%s", unameStruct.release));
            break;
        default:
            info->distro = DISTRO_UNKNOWN;
        }
    }

    if(info->distro == DISTRO_UNKNOWN)
    {
        CT_SAFE_FREE_STRING(info->version);
        GCE(ceError = CTStrdup("unknown", &info->version));
    }

    //Check for version override file
    GCE(ceError = CTAllocateStringPrintf(
            &path, "%s/osver", testPrefix));
    GCE(ceError = CTCheckFileOrLinkExists(
            path, &exists));
    if(exists)
    {
        GCE(ceError = CTReadFile(
                path, SIZE_MAX, &fileContents, NULL));
    }
    if(fileContents != NULL)
    {
        CTStripWhitespace(fileContents);
        CT_SAFE_FREE_STRING(info->version);
        info->version = fileContents;
        fileContents = NULL;
    }
    CT_SAFE_FREE_STRING(path);

    /*
    uname -m output:
    Linux: x86_64
    Linux: i386
    Linux: i686
    AIX: 00CBE1DD4C00
    Solaris: sun4u
    Solaris: i86pc
    Darwin: i386
    Darwin: Power Macintosh
    HPUX: 9000/785

    uname -i output:
    Linux: x86_64
    Linux: i386
    RHEL21: not recogn
    AIX: not recogn
    Darwin: not recogn
    Solaris: SUNW,Ultra-4
    Solaris: i86pc
    HPUX: 2000365584

    uname -p output:
    Linux reads /proc/cpuinfo
    Linux: x86_64
    Linux: i686
    Linux: athlon
    Darwin: i386
    Darwin: powerpc
    AIX has the value hard coded in uname
    AIX: powerpc
    Solaris uses sysinfo(SI_ARCHITECTURE, buff, sizeof(buff)
    Solaris: sparc
    Solaris: i386
    HPUX: not recogn
    */
    info->arch = ARCH_UNKNOWN;
#if defined(HAVE_SYSINFO) && defined(SI_ARCHITECTURE)
    //Solaris has this
    if(info->arch == ARCH_UNKNOWN &&
            sysinfo(SI_ARCHITECTURE, archBuffer, sizeof(archBuffer)) != -1)
    {
        info->arch = CTGetArchFromString(archBuffer);
    }
#endif
#if defined(HAVE_SYSCONF) && defined(_SC_CPU_VERSION)
    //HPUX uses this
    if(info->arch == ARCH_UNKNOWN)
    {
        switch(sysconf(_SC_CPU_VERSION))
        {
            case CPU_PA_RISC1_0:
            case CPU_PA_RISC1_1:
            case CPU_PA_RISC1_2:
            case CPU_PA_RISC2_0:
            case CPU_PA_RISC_MAX:
                info->arch = ARCH_HPPA;
                break;
#ifdef CPU_HP_INTEL_EM_1_0
            case CPU_HP_INTEL_EM_1_0:
#endif
#ifdef CPU_IA64_ARCHREV_0
            case CPU_IA64_ARCHREV_0:
#endif
                info->arch = ARCH_IA64;
                break;
            //If it's not any of the previous values, let another test figure
            //it out.
        }
    }
#endif
    if(info->arch == ARCH_UNKNOWN)
    {
        //Linux uses this, and sometimes Darwin does too. If 'uname -m' doesn't
        //return something meaningful on this platform, then the arch will stay
        //as unknown.
        info->arch = CTGetArchFromString(unameStruct.machine);
    }
    if(info->arch == ARCH_UNKNOWN)
    {
        //AIX and sometimes Darwin use this
        GCE(ceError = CTCaptureOutput("uname -p", &distroString));
        CTStripWhitespace(distroString);
        info->arch = CTGetArchFromString(distroString);
        CT_SAFE_FREE_STRING(distroString);
    }

    //Check for arch override file
    GCE(ceError = CTAllocateStringPrintf(
            &path, "%s/osarch", testPrefix));
    GCE(ceError = CTCheckFileOrLinkExists(
            path, &exists));
    if(exists)
    {
        GCE(ceError = CTReadFile(
                path, SIZE_MAX, &fileContents, NULL));
        info->arch = CTGetArchFromString(fileContents);
        CT_SAFE_FREE_STRING(fileContents);
    }
    CT_SAFE_FREE_STRING(path);

cleanup:
    CT_SAFE_FREE_STRING(path);
    CT_SAFE_FREE_STRING(fileContents);
    CT_SAFE_FREE_STRING(distroString);
    if(rxAlloced)
        regfree(&rx);
    if(ceError)
    {
        CTFreeDistroInfo(info);
    }
    return ceError;
}