Пример #1
0
DWORD LWExceptionToString(const LWException *conv, PCSTR titlePrefix, BOOLEAN showSymbolicCode, BOOLEAN showTrace, PSTR *result)
{
    DWORD ceError;
    PSTR ret = NULL;
    PSTR temp = NULL;
    PCSTR codeName = NULL;

    if(titlePrefix == NULL)
        titlePrefix = "";
    
    if(showSymbolicCode)
        codeName = LwWin32ExtErrorToName(conv->code);

    if(codeName != NULL)
    {
        GCE(ceError = CTAllocateStringPrintf(
            &ret, "%s%s [%s]\n\n%s", titlePrefix, conv->shortMsg, codeName, conv->longMsg));
    }
    else
    {
        GCE(ceError = CTAllocateStringPrintf(
            &ret, "%s%s [code 0x%.8x]\n\n%s", titlePrefix, conv->shortMsg, conv->code, conv->longMsg));
    }
    if(showTrace)
    {
        const LWStackFrame *frame = &conv->stack;

        temp = ret;
        GCE(ceError = CTAllocateStringPrintf(
                &ret, "%s\n\nStack Trace:", temp));
        CT_SAFE_FREE_STRING(temp);
        while(frame != NULL)
        {
            temp = ret;
            GCE(ceError = CTAllocateStringPrintf(
                    &ret, "%s\n%s:%d", temp, frame->file, frame->line));
            CT_SAFE_FREE_STRING(temp);
            frame = frame->down;
        }
    }
    *result = ret;
    ret = NULL;

cleanup:
    CT_SAFE_FREE_STRING(temp);
    CT_SAFE_FREE_STRING(ret);
    return ceError;
}
Пример #2
0
DWORD
DJGetFinalFqdn(
    const JoinProcessOptions *options,
    PSTR *fqdn
    )
{
    DWORD ceError = ERROR_SUCCESS;
    const ModuleState *state = DJGetModuleStateByName((JoinProcessOptions *)options, "hostname");

    *fqdn = NULL;

    if(state != NULL && state->disposition == EnableModule)
    {
        //The fqdn will be set
        ceError = CTAllocateStringPrintf(fqdn, "%s.%s", options->computerName, options->domainName);
        GCE(ceError);
    }
    else
    {
        //The fqdn will not be set
        GCE(ceError = DJGetFQDN(NULL, fqdn));
    }
cleanup:
    return ceError;
}
Пример #3
0
DWORD
DJSetOptionValue(DynamicArray *lines, PCSTR stanza, PCSTR name, PCSTR value)
{
    ssize_t index;
    PSTR newLine = NULL;
    DWORD ceError = ERROR_SUCCESS;

    if(strchr(value, ' '))
    {
        /* Fixes bug 5564 */
        GCE(ceError = CTAllocateStringPrintf(&newLine, "\t%s = \"%s\"\n", name, value));
    }
    else
        GCE(ceError = CTAllocateStringPrintf(&newLine, "\t%s = %s\n", name, value));

    index = DJFindLine(lines, stanza, name);

    if(index == -1)
    {
        index = DJFindStanza(lines, stanza);
        if(index == -1)
            GCE(ceError = ERROR_INVALID_OPERATION);
        index++;
    }
    else
    {
        CT_SAFE_FREE_STRING(*(PSTR *)CTArrayGetItem((DynamicArray*)lines,
                    index, sizeof(PSTR)));
        GCE(ceError = CTArrayRemove((DynamicArray *)lines, index,
                    sizeof(PSTR), 1));
    }

    GCE(ceError = CTArrayInsert((DynamicArray *)lines, index,
                sizeof(PSTR), &newLine, 1));

cleanup:
    return ceError;
}
Пример #4
0
MU_TEST(CTFileUtils, CTCreateDirectory)
{
    PSTR pszPath = NULL;
    PSTR pszBasePath = NULL;
    struct stat statbuf;

    MU_TRY_CTERR(CTAllocateStringPrintf(
                     &pszBasePath,
                     "/tmp/ctfileutils-%lu",
                     (unsigned long) getpid()));

    MU_TRY_CTERR(CTAllocateStringPrintf(
                     &pszPath,
                     "%s/foo/bar/bob",
                     pszBasePath));

    MU_TRY_CTERR(CTCreateDirectory(pszPath, 0700));

    MU_TRY_ERRNO(stat(pszPath, &statbuf));
    MU_ASSERT(S_ISDIR(statbuf.st_mode));
    MU_ASSERT((statbuf.st_mode & 0777) == 0700);
    MU_TRY_CTERR(CTRemoveFiles(pszBasePath, FALSE, TRUE));
}
Пример #5
0
static PSTR GetPamModeDescription(const JoinProcessOptions *options, LWException **exc)
{
    PSTR ret = NULL;
    LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf( &ret,
"By default AIX will use LAM to perform authentication requests, but AIX 5.3 may instead use PAM for authentication. PAM allows for richer text in prompts and error messages, so it is recommended that the system be switched into PAM mode. This will be done by setting 'auth_type = PAM_AUTH' in the 'usw' stanza in /etc/security/login.cfg.\n"
"\n"
"A few of the programs that ship with AIX are not enabled in the default pam.conf. Before switching the system into PAM mode, entries are made in pam.conf for these services:"
"\tsshd\n"
"\tsudo\n"
"\tdtsession"));

cleanup:
    return ret;
}
Пример #6
0
static DWORD WriteNsswitchConfiguration(const char *rootPrefix, NsswitchConf *conf)
{
    DWORD ceError = ERROR_SUCCESS;
    DynamicArray printedLine;
    int i;
    char *tempName = NULL;
    char *finalName = NULL;
    char *prefixedPath = NULL;
    FILE *file = NULL;
    memset(&printedLine, 0, sizeof(printedLine));

    GCE(ceError = CTAllocateStringPrintf(&prefixedPath, "%s%s", rootPrefix, conf->filename));

    GCE(ceError = CTGetFileTempPath(
                        prefixedPath,
                        &finalName,
                        &tempName));

    DJ_LOG_INFO("Writing nsswitch configuration for %s", finalName);

    ceError = CTOpenFile(tempName, "w", &file);
    if(ceError)
    {
        DJ_LOG_ERROR("Unable to open '%s' for writing", tempName);
        GCE(ceError);
    }

    for(i = 0; i < conf->lines.size; i++)
    {
        GCE(ceError = GetPrintedLine(&printedLine, conf, i));
        GCE(ceError = CTFilePrintf(file, "%s\n", printedLine.data));
    }

    GCE(ceError = CTCloseFile(file));
    file = NULL;

    GCE(ceError = CTSafeReplaceFile(
            finalName,
            tempName));
    DJ_LOG_INFO("File moved into place");

cleanup:
    if(file != NULL)
        CTCloseFile(file);
    CTArrayFree(&printedLine);
    CT_SAFE_FREE_STRING(tempName);
    CT_SAFE_FREE_STRING(finalName);
    CT_SAFE_FREE_STRING(prefixedPath);
    return ceError;
}
Пример #7
0
JoinProgressDialog*
joinprogress_new(GtkWindow* parent, const char* title)
{
    GladeXML* xml = glade_xml_new (DOMAINJOIN_XML, "JoinProgressDialog", NULL);
    JoinProgressDialog* dialog = g_new0(JoinProgressDialog, 1);
    char* title_markup;

    if(!xml || !dialog)
        goto cleanup;

    dialog->dialog = GTK_DIALOG(glade_xml_get_widget(xml, "JoinProgressDialog"));
    g_assert(dialog->dialog != NULL);
    g_object_ref(G_OBJECT(dialog->dialog));

    gtk_window_set_transient_for (GTK_WINDOW(dialog->dialog), parent);

    dialog->status = GTK_LABEL(glade_xml_get_widget(xml, "JoinProgressStatus"));
    g_assert(dialog->status != NULL);
    g_object_ref(G_OBJECT(dialog->status));

    dialog->title = GTK_LABEL(glade_xml_get_widget(xml, "JoinProgressTitle"));
    g_assert(dialog->title != NULL);
    g_object_ref(G_OBJECT(dialog->title));

    if (CTAllocateStringPrintf(&title_markup, "<span weight=\"bold\" size=\"x-large\">%s</span>", title))
        return NULL;

    gtk_label_set_markup(dialog->title, title_markup);

    CTFreeString(title_markup);

    dialog->progress = GTK_PROGRESS_BAR(glade_xml_get_widget(xml, "JoinProgressBar"));
    g_assert(dialog->progress != NULL);
    g_object_ref(G_OBJECT(dialog->progress));

    dialog->close = GTK_BUTTON(glade_xml_get_widget(xml, "JoinProgressClose"));
    g_assert(dialog->close != NULL);
    g_object_ref(G_OBJECT(dialog->close));

cleanup:
    if (xml)
    {
        g_object_unref(xml);
        xml = NULL;
    }
    return dialog;
}
Пример #8
0
static DWORD ReadNsswitchFile(NsswitchConf *conf, const char *rootPrefix, const char *filename)
{
    DWORD ceError = ERROR_SUCCESS;
    FILE *file = NULL;
    PSTR buffer = NULL;
    char *fullPath = NULL;
    BOOLEAN endOfFile = FALSE;
    BOOLEAN exists;

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

    GCE(ceError = CTAllocateStringPrintf(
            &fullPath, "%s%s", rootPrefix, filename));
    DJ_LOG_INFO("Reading nsswitch file %s", fullPath);
    GCE(ceError = CTCheckFileOrLinkExists(fullPath, &exists));
    if(!exists)
    {
        DJ_LOG_INFO("File %s does not exist", fullPath);
        ceError = ERROR_FILE_NOT_FOUND;
        goto cleanup;
    }

    GCE(ceError = CTStrdup(filename,
        &conf->filename));
    GCE(ceError = CTOpenFile(fullPath, "r", &file));
    CT_SAFE_FREE_STRING(fullPath);
    while(TRUE)
    {
        CT_SAFE_FREE_STRING(buffer);
        GCE(ceError = CTReadNextLine(file, &buffer, &endOfFile));
        if(endOfFile)
            break;
        GCE(ceError = AddFormattedLine(conf, filename, buffer, NULL));
    }

    conf->modified = FALSE;

cleanup:
    CT_SAFE_FREE_STRING(buffer);
    if(file != NULL)
        CTCloseFile(file);
    CT_SAFE_FREE_STRING(fullPath);
    if(ceError)
        FreeNsswitchConfContents(conf);
    return ceError;
}
Пример #9
0
static DWORD ReadSshFile(struct SshConf *conf, const char *rootPrefix, const char *filename)
{
    DWORD ceError = ERROR_SUCCESS;
    FILE *file = NULL;
    PSTR buffer = NULL;
    char *fullPath = NULL;
    BOOLEAN endOfFile = FALSE;
    BOOLEAN exists;

    BAIL_ON_CENTERIS_ERROR(ceError = CTAllocateStringPrintf(
            &fullPath, "%s%s", rootPrefix, filename));
    DJ_LOG_INFO("Reading ssh file %s", fullPath);
    BAIL_ON_CENTERIS_ERROR(ceError = CTCheckFileOrLinkExists(fullPath, &exists));
    if(!exists)
    {
        DJ_LOG_INFO("File %s does not exist", fullPath);
        ceError = ERROR_FILE_NOT_FOUND;
        goto error;
    }

    BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(filename,
        &conf->filename));
    BAIL_ON_CENTERIS_ERROR(ceError = CTOpenFile(fullPath, "r", &file));
    CT_SAFE_FREE_STRING(fullPath);
    while(TRUE)
    {
        CT_SAFE_FREE_STRING(buffer);
        BAIL_ON_CENTERIS_ERROR(ceError = CTReadNextLine(file, &buffer, &endOfFile));
        if(endOfFile)
            break;
        BAIL_ON_CENTERIS_ERROR(ceError = AddFormattedLine(conf, filename, buffer, NULL));
    }
	CT_SAFE_FREE_STRING(buffer);
    BAIL_ON_CENTERIS_ERROR(ceError = CTCloseFile(file));
    file = NULL;

    return ceError;

error:
    if(file != NULL)
        CTCloseFile(file);
    CT_SAFE_FREE_STRING(fullPath);
    FreeSshConfContents(conf);
    CT_SAFE_FREE_STRING(buffer);
    return ceError;
}
Пример #10
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);
}
Пример #11
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;
}
Пример #12
0
BOOLEAN FindSshAndConfig(
        PCSTR pSshOrSshd,
        PSTR *ppSshBinary,
        PSTR *ppSshConfig,
        LWException **ppExc)
{
    DWORD ceError = ERROR_SUCCESS;
    /* Mac OS X stores the configuration in /etc */
    /* CSW ssh on Solaris uses /opt/csw/etc/ssh */
    const PCSTR ppSshConfigPaths[] = {
        "/etc/ssh",
        "/opt/ssh/etc",
        "/usr/local/etc",
        "/etc",
        "/etc/openssh",
        "/usr/openssh/etc",
        "/opt/csw/etc/ssh",
        NULL
    };
    DWORD foundConfigCount = 0;
    PSTR* ppFoundConfigs = NULL;

    /* Solaris Sparc 10 stores sshd in /usr/lib/ssh */
    /* Experian says their sshd is at /usr/openssh/sbin/sshd */
    /* CSW ssh on Solaris uses /opt/csw/sbin/sshd */
    const PCSTR ppSshBinaryPaths[] = {
        "/usr/sbin",
        "/opt/ssh/sbin",
        "/usr/local/sbin",
        "/usr/bin",
        "/opt/ssh/bin",
        "/usr/local/bin",
        "/usr/lib/ssh",
        "/usr/openssh/sbin",
        "/usr/openssh/bin",
        "/opt/csw/sbin",
        "/opt/csw/bin",
        "/opt/ssh/hpux64/sbin",
        NULL
    };
    DWORD foundBinaryCount = 0;
    PSTR* ppFoundBinaries = NULL;
    PSTR pConfigFilename = NULL;
    PSTR pBinaryFilename = NULL;
    PSTR pFoundConfigList = NULL;
    PSTR pFoundBinaryList = NULL;
    DWORD index = 0;

    *ppSshBinary = NULL;
    *ppSshConfig = NULL;

    LW_CLEANUP_CTERR(ppExc, CTAllocateStringPrintf(
        &pConfigFilename, "%s_config", pSshOrSshd));
    LW_CLEANUP_CTERR(ppExc, CTAllocateStringPrintf(
        &pBinaryFilename, "%s", pSshOrSshd));

    ceError = LwFindFilesInPaths(
                    pConfigFilename,
                    LWFILE_REGULAR,
                    ppSshConfigPaths,
                    &foundConfigCount,
                    &ppFoundConfigs);
    LW_CLEANUP_CTERR(ppExc, ceError);

    ceError = LwRemoveDuplicateInodes(
                    &foundConfigCount,
                    ppFoundConfigs);
    LW_CLEANUP_CTERR(ppExc, ceError);

    ceError = LwFindFilesInPaths(
                    pBinaryFilename,
                    LWFILE_REGULAR,
                    ppSshBinaryPaths,
                    &foundBinaryCount,
                    &ppFoundBinaries);
    LW_CLEANUP_CTERR(ppExc, ceError);

    ceError = LwRemoveDuplicateInodes(
                    &foundBinaryCount,
                    ppFoundBinaries);
    LW_CLEANUP_CTERR(ppExc, ceError);

    // Ssh version A.04.40.005 on HP-UX Itanimum has these paths:
    //  /opt/ssh/hpux64/sbin/sshd (ELF-64 IA64)
    //  /opt/ssh/sbin/sshd (ELF-32 IA64)
    //  /usr/sbin/sshd (symlink to /opt/ssh/hpux64/sbin/sshd)
    //
    // Ssh version A.04.30.007 on HP-UX Itanimum has these paths:
    //  /opt/ssh/hpux64/sbin/sshd (ELF-64 IA64)
    //  /opt/ssh/sbin/sshd (ELF-32 IA64)
    //  /usr/sbin/sshd (symlink to /opt/ssh/sbin/sshd)
    //
    // Both files point to the same config path. An exception must exist for
    // this case since the inodes are different for the two architectures.
    //
    // The symlink at /usr/sbin/sshd wins the duplication removal process. So
    // if it is found, the other path must be removed.
    
    for (index = 0; index < foundBinaryCount; index++)
    {
        if (!strcmp(ppFoundBinaries[index], "/usr/sbin/sshd"))
        {
            // Remove /opt/ssh/sbin/sshd or /opt/ssh/hpux64/sbin/sshd if they
            // are in the list
            for (index = 0; index < foundBinaryCount; index++)
            {
                if (!strcmp(ppFoundBinaries[index], "/opt/ssh/sbin/sshd") ||
                    !strcmp(ppFoundBinaries[index],
                        "/opt/ssh/hpux64/sbin/sshd"))
                {
                    LW_SAFE_FREE_STRING(ppFoundBinaries[index]);
                    memmove(&ppFoundBinaries[index],
                            &ppFoundBinaries[index+1],
                            (foundBinaryCount - index - 1) *
                                sizeof(ppFoundBinaries[index]));
                    foundBinaryCount--;
                    break;
                }
            }
            break;
        }
    }

    if ((foundConfigCount | foundBinaryCount) > 0 && foundConfigCount != foundBinaryCount)
    {
        ceError = CombinePaths(
                        foundConfigCount,
                        ppFoundConfigs,
                        &pFoundConfigList);
        LW_CLEANUP_CTERR(ppExc, ceError);

        ceError = CombinePaths(
                        foundBinaryCount,
                        ppFoundBinaries,
                        &pFoundBinaryList);
        LW_CLEANUP_CTERR(ppExc, ceError);

        if ((foundConfigCount | foundBinaryCount) == 1)
        {
            ceError = ERROR_FILE_NOT_FOUND;
        }
        else
        {
            ceError = ERROR_DUPLICATE_SERVICE_NAME;
        }

        LW_RAISE_EX(ppExc, ceError,
                "Unable to find ssh binary",
"A %s config file was at %s, and a %s binary file was found at %s. Exactly one config file and one binary must exist on the system in a standard location. Uninstall any unused copies of ssh.",
                pSshOrSshd,
                pFoundConfigList,
                pSshOrSshd,
                pFoundBinaryList);
    }

    if (foundConfigCount == 1)
    {
        DJ_LOG_INFO("Found config file %s", ppFoundConfigs[0]);
        *ppSshConfig = ppFoundConfigs[0];
        ppFoundConfigs[0] = NULL;
    }
    if (foundBinaryCount == 1)
    {
        DJ_LOG_INFO("Found binary %s", ppFoundBinaries[0]);
        *ppSshBinary = ppFoundBinaries[0];
        ppFoundBinaries[0] = NULL;
    }

cleanup:
    if (ppFoundConfigs)
    {
        LwFreeStringArray(
                ppFoundConfigs,
                foundConfigCount);
    }
    if (ppFoundBinaries)
    {
        LwFreeStringArray(
                ppFoundBinaries,
                foundBinaryCount);
    }
    LW_SAFE_FREE_STRING(pConfigFilename);
    LW_SAFE_FREE_STRING(pBinaryFilename);
    LW_SAFE_FREE_STRING(pFoundConfigList);
    LW_SAFE_FREE_STRING(pFoundBinaryList);
    return (foundConfigCount == 1);
}
Пример #13
0
/* Copy a ssh configuration line and add it below the old line. */
static DWORD SetOption(struct SshConf *conf, const char *name, const char *value)
{
    DWORD ceError = ERROR_SUCCESS;
    int line = -1;
    DynamicArray printedLine;
    struct SshLine lineObj;
    int found = 0;

    memset(&lineObj, 0, sizeof(struct SshLine));
    memset(&printedLine, 0, sizeof(printedLine));

    for(line = 0; line < conf->lineCount; line++)
    {
        line = FindOption(conf, line, name);
        if(line == -1)
            break;
        found++;
        if(!strcmp(conf->lines[line].value.value, value))
            continue;

        //Insert a commented out version of the line
        BAIL_ON_CENTERIS_ERROR(ceError = GetPrintedLine(&printedLine, conf, line));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.leadingWhiteSpace));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.name.value));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.name.trailingSeparator));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.value.value));
        BAIL_ON_CENTERIS_ERROR(ceError = CTAllocateStringPrintf(
            &lineObj.value.trailingSeparator,
            "#Overwritten by lwidentity: %s",
            printedLine.data));
        BAIL_ON_CENTERIS_ERROR(ceError = CTArrayInsert(&conf->private_data,
                    line, sizeof(struct SshLine), &lineObj, 1));
        memset(&lineObj, 0, sizeof(lineObj));
        UpdatePublicLines(conf);
        conf->modified = 1;
        line++;

        //Change the option value of the line
        CT_SAFE_FREE_STRING(conf->lines[line].value.value);
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(value,
            &conf->lines[line].value.value));
    }

    /*If the option wasn't already in the file, search for comments that
      mention the option, and insert the line after the comment*/
    for(line = 0; !found && line < conf->lineCount; line++)
    {
        if(strstr(conf->lines[line].value.trailingSeparator, name) == NULL)
            continue;

        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.leadingWhiteSpace));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(name,
            &lineObj.name.value));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(" ",
            &lineObj.name.trailingSeparator));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(value,
            &lineObj.value.value));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.value.trailingSeparator));
        BAIL_ON_CENTERIS_ERROR(ceError = CTArrayInsert(&conf->private_data,
                    line + 1, sizeof(struct SshLine), &lineObj, 1));
        memset(&lineObj, 0, sizeof(lineObj));
        conf->modified = 1;
        found++;
    }

    /*If the option wasn't even in a comment, just add the option at the
      end of the file
      */
    if(!found)
    {
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.leadingWhiteSpace));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(name,
            &lineObj.name.value));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(" ",
            &lineObj.name.trailingSeparator));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup(value,
            &lineObj.value.value));
        BAIL_ON_CENTERIS_ERROR(ceError = CTStrdup("",
            &lineObj.value.trailingSeparator));
        BAIL_ON_CENTERIS_ERROR(ceError = CTArrayAppend(&conf->private_data,
                    sizeof(struct SshLine), &lineObj, 1));
        memset(&lineObj, 0, sizeof(lineObj));
        conf->modified = 1;
    }

error:
    UpdatePublicLines(conf);
    FreeSshLineContents(&lineObj);
    CTArrayFree(&printedLine);
    return ceError;
}
Пример #14
0
static
DWORD
DJFixNetworkManagerOnlineTimeout(
    )
{
    DWORD ceError = ERROR_SUCCESS;
    PSTR pszFilePath = "/etc/sysconfig/network/config";
    DWORD dwTimeout = 60;
    int EE = 0;
    BOOLEAN bFileExists = FALSE;
    char *isEnabled = NULL;
    char *currentTimeout = NULL;
    char *sedExpression = NULL;
    long currentTimeoutLong;
    char *conversionEnd;

    ceError = CTCheckFileExists(pszFilePath, &bFileExists);
    CLEANUP_ON_DWORD_EE(ceError, EE);
    if(!bFileExists)
        goto cleanup;

    ceError = CTShell(". %pszFilePath; echo \"$NETWORKMANAGER\" >%enabled; echo \"$NM_ONLINE_TIMEOUT\" >%timeout",
            CTSHELL_STRING (pszFilePath, pszFilePath),
            CTSHELL_BUFFER (enabled, &isEnabled),
            CTSHELL_BUFFER (timeout, &currentTimeout));
    CLEANUP_ON_DWORD_EE(ceError, EE);
    CTStripTrailingWhitespace(isEnabled);
    CTStripTrailingWhitespace(currentTimeout);

    DJ_LOG_VERBOSE("Network manager enabled [%s] network manager timeout [%s]", isEnabled, currentTimeout);

    if(strcasecmp(isEnabled, "yes"))
    {
        DJ_LOG_INFO("Network manager is not enabled");
        goto cleanup;
    }

    currentTimeoutLong = strtol(currentTimeout, &conversionEnd, 10);
    if(*conversionEnd != '\0')
    {
        DJ_LOG_INFO("Unable to convert network manager timeout to long");
        currentTimeoutLong = 0;
    }

    if(currentTimeoutLong < dwTimeout)
    {
        DJ_LOG_INFO("Setting network manager timeout to %d", dwTimeout);
        ceError = CTAllocateStringPrintf(&sedExpression,
                "s/^\\([ \t]*NM_ONLINE_TIMEOUT[ \t]*=[ \t]*\\).*$/\\1%d/",
                dwTimeout);
        CLEANUP_ON_DWORD_EE(ceError, EE);
        ceError = CTRunSedOnFile(pszFilePath, pszFilePath, FALSE, sedExpression);
        CLEANUP_ON_DWORD_EE(ceError, EE);
    }

cleanup:
    DJ_LOG_VERBOSE("DJFixNetworkManagerOnlineTimeout LEAVE -> 0x%08x (EE = %d)", ceError, EE);

    CT_SAFE_FREE_STRING(isEnabled);
    CT_SAFE_FREE_STRING(currentTimeout);
    CT_SAFE_FREE_STRING(sedExpression);

    return ceError;
}
Пример #15
0
static QueryResult QueryDescriptionSetHostname(const JoinProcessOptions *options, PSTR *changeDescription, LWException **exc)
{
    PSTR required = NULL;
    PSTR optional = NULL;
    PSTR both = NULL;
    PSTR newFqdn = NULL;
    PSTR oldShortHostname = NULL;
    PSTR oldFqdnHostname = NULL;
    PSTR newValue;
    PHOSTSFILELINE pHostsFileLineList = NULL;
    BOOLEAN describedFqdn = FALSE;
    QueryResult result = CannotConfigure;
    BOOLEAN modified = FALSE;
    DWORD ceError = ERROR_SUCCESS;

    if(!options->joiningDomain || options->enableMultipleJoins)
    {
        if(changeDescription != NULL)
        {
            LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(changeDescription,
                "The hostname is fully set"));
        }
        result = NotApplicable;
        goto cleanup;
    }

    //Throws an exception if the hostname is not valid
    LW_TRY(exc, DJCheckValidComputerName(options->computerName, &LW_EXC));

    LW_CLEANUP_CTERR(exc, CTStrdup("", &required));
    LW_CLEANUP_CTERR(exc, CTStrdup("", &optional));
//    LW_CLEANUP_CTERR(exc, CTStrdup("", &both));

    LW_CLEANUP_CTERR(exc, DJGetFQDN(&oldShortHostname, &oldFqdnHostname));
    CTStrToLower(oldShortHostname);
    CTStrToLower(oldFqdnHostname);
    if(strcasecmp(oldShortHostname, options->computerName))
    {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&newValue,
"%s\n"
"\tSet the computer short hostname (currently %s) to the requested hostname (%s) by setting:\n"
"\t\t* The kernel hostname\n"
"\t\t* The hostname in files for statically configured computers\n"
"\t\t* The hostname in files for DHCP configured computers",
            required, oldShortHostname, options->computerName));
        CT_SAFE_FREE_STRING(required);
        required = newValue;
    }

    LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&newFqdn,
                "%s.%s", options->computerName,
            options->domainName));
    CTStrToLower(newFqdn);
    if(oldFqdnHostname == NULL)
    {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&newValue,
"%s\n"
"\tGive the machine a fully-qualified domain name. If performed automatically, the fqdn will be set through /etc/hosts to '%s', but it is possible to use a different fqdn and/or set it through dns instead of /etc/hosts. However in all cases, the fqdn must follow standard DNS naming conventions, and have a period in the name. The following steps will be used if the fqdn is set automatically:\n"
"\t\t* Make sure local comes before bind in nsswitch\n"
"\t\t* Add a loopback entry in /etc/hosts and put the fqdn as the primary name\n",
            required, newFqdn));
        CT_SAFE_FREE_STRING(required);
        required = newValue;
        describedFqdn = TRUE;
    }
    else if(strcmp(newFqdn, oldFqdnHostname))
    {
        //The current fqdn does not match our ideal fqdn
#if defined(__LWI_DARWIN__)
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&newValue,
"%s\n"
"\tChange the fqdn from '%s' to '%s'. This could be done via DNS, but this program will do it with the following steps:\n"
"\t\t* Making sure local comes before bind in nsswitch\n"
"\t\t* Adding the fqdn before all entries in /etc/hosts that contain the short hostname and removing the old fqdn if it appears on the line\n"
"\t\t* Sending hang-up command to mDNSResponder to flush the DNS cache",
            optional, oldFqdnHostname, newFqdn));
#else
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&newValue,
"%s\n"
"\tChange the fqdn from '%s' to '%s'. This could be done via DNS, but this program will do it with the following steps:\n"
"\t\t* Making sure local comes before bind in nsswitch\n"
"\t\t* Adding the fqdn before all entries in /etc/hosts that contain the short hostname and removing the old fqdn if it appears on the line\n"
"\t\t* Restart nscd (if running) to flush the DNS cache",
            optional, oldFqdnHostname, newFqdn));
#endif
        CT_SAFE_FREE_STRING(optional);
        optional = newValue;
        describedFqdn = TRUE;
    }

    //Find out if the fqdn is stored in /etc/hosts
    if(oldFqdnHostname != NULL && !strcmp(oldFqdnHostname, "localhost"))
    {
        CT_SAFE_FREE_STRING(oldFqdnHostname);
    }
    if(oldShortHostname != NULL && !strcmp(oldShortHostname, "localhost"))
    {
        CT_SAFE_FREE_STRING(oldShortHostname);
    }

    LW_CLEANUP_CTERR(exc, DJParseHostsFile("/etc/hosts", &pHostsFileLineList));
    LW_CLEANUP_CTERR(exc, DJReplaceHostnameInMemory(
                pHostsFileLineList,
                oldShortHostname, oldFqdnHostname,
                options->computerName, options->domainName));
    modified |= DJHostsFileWasModified(pHostsFileLineList);
    if (pHostsFileLineList)
    {
        DJFreeHostsFileLineList(pHostsFileLineList);
        pHostsFileLineList = NULL;
    }
    ceError = DJParseHostsFile("/etc/inet/ipnodes", &pHostsFileLineList);
    if(ceError == ERROR_FILE_NOT_FOUND)
    {
        ceError = ERROR_SUCCESS;
    }
    else if(!ceError)
    {
        LW_CLEANUP_CTERR(exc, DJReplaceHostnameInMemory(
                    pHostsFileLineList,
                    oldShortHostname, oldFqdnHostname,
                    options->computerName, options->domainName));
        modified |= DJHostsFileWasModified(pHostsFileLineList);
    }
    LW_CLEANUP_CTERR(exc, ceError);

    if (modified && !describedFqdn) {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&newValue,
"%s\n"
"\tSet the fqdn in /etc/hosts and /etc/inet/ipnodes. This will preserve the fqdn even when the DNS server is unreachable. This will be performed with these steps:\n"
"\t\t* Making sure local comes before bind in nsswitch\n"
"\t\t* Adding the fqdn before all entries in /etc/hosts and /etc/inet/ipnodes that contain the short hostname and removing the old fqdn if it appears on the line\n"
"\t\t* Restart nscd (if running) to flush the DNS cache"
        , optional, oldFqdnHostname, newFqdn));
        CT_SAFE_FREE_STRING(optional);
        optional = newValue;
        describedFqdn = TRUE;
    }

    if(strlen(required) > 1)
    {
        if(strlen(optional) > 1)
        {
            LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&both,
"The following step(s) are required:%s\n\nThe following step(s) are optional (but will performed automatically when this step is run):%s", required, optional));
        }
        else
        {
            LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&both,
"The following step(s) are required:%s", required));
        }
        result = NotConfigured;
    }
    else if(strlen(optional) > 1)
    {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&both,
"The following step(s) are optional:%s", optional));
        result = SufficientlyConfigured;
    }
    else
    {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&both,
"The hostname is fully set"));
        result = FullyConfigured;
    }

    if(changeDescription != NULL)
    {
        *changeDescription = both;
        both = NULL;
    }

cleanup:
    CT_SAFE_FREE_STRING(required);
    CT_SAFE_FREE_STRING(optional);
    CT_SAFE_FREE_STRING(both);
    CT_SAFE_FREE_STRING(oldShortHostname);
    CT_SAFE_FREE_STRING(oldFqdnHostname);
    CT_SAFE_FREE_STRING(newFqdn);
    if (pHostsFileLineList)
        DJFreeHostsFileLineList(pHostsFileLineList);

    return result;
}
Пример #16
0
DWORD
CTGetPidOfCmdLine(
    PCSTR programName,
    PCSTR programFilename,
    PCSTR cmdLine,
    uid_t owner,
    pid_t *pid,
    size_t *count
    )
{
    DWORD ceError = ERROR_NOT_SUPPORTED;
    size_t fillCount = 0;
    size_t foundCount = 0;
    struct stat findStat;
#if HAVE_DECL_PSTAT_GETPROC
    //HPUX should have this
    struct pst_status mystatus;
    struct pst_status status[10];
    int inBuffer;
    int i;
#endif
#ifdef HAVE_STRUCT_PSINFO
    //Solaris and AIX should have this
    DIR *dir = NULL;
    struct dirent *dirEntry = NULL;
    PSTR filePath = NULL;
    struct psinfo infoStruct;
    FILE *infoFile = NULL;
    struct stat compareStat;
    BOOLEAN bFileExists;
#endif
#if defined(HAVE_KVM_GETPROCS) && HAVE_DECL_KERN_PROC_PATHNAME
    //FreeBSD has this
    char pathBuffer[MAXPATHLEN];
    size_t len;
    int unfilteredCount;
    kvm_t *kd = NULL;
    struct kinfo_proc *procs;
    int i;
    struct kinfo_proc *pos;
    int sysctlName[4] = {
        CTL_KERN,
        KERN_PROC,
        KERN_PROC_PATHNAME,
        0 };
#endif

    if(count)
    {
        fillCount = *count;
        *count = 0;
    }
    else if(pid != NULL)
        fillCount = 1;

    if(programFilename != NULL)
    {
        while(stat(programFilename, &findStat) < 0)
        {
            if(errno == EINTR)
                continue;
            GCE(ceError = LwMapErrnoToLwError(errno));
        }
    }
    
#if HAVE_DECL_PSTAT_GETPROC
    //First get the process info for this process
    inBuffer = pstat_getproc(&mystatus, sizeof(mystatus), 0,
            getpid());
    if(inBuffer != 1)
        GCE(ceError = LwMapErrnoToLwError(errno));

    //Now look at all processes
    inBuffer = pstat_getproc(status, sizeof(status[0]),
            sizeof(status)/sizeof(status[0]), 0);
    if(inBuffer < 0)
        GCE(ceError = LwMapErrnoToLwError(errno));
    while(inBuffer > 0)
    {
        for(i = 0; i < inBuffer; i++)
        {
            if(memcmp(&mystatus.pst_rdir, &status[i].pst_rdir,
                        sizeof(mystatus.pst_rdir)))
            {
                /* This process has a different root directory (it is being run
                   via a chroot). Let's not count this process as a match. */
                continue;
            }
            if (owner != (uid_t)-1 && owner != status[i].pst_euid)
            {
                continue;
            }
            if (programName != NULL && strcmp(status[i].pst_ucomm, programName))
            {
                continue;
            }
            if (cmdLine != NULL && strcmp(status[i].pst_cmd, cmdLine))
            {
                continue;
            }
            if(programFilename != NULL && (
                    status[i].pst_text.psf_fileid != findStat.st_ino ||
                    status[i].pst_text.psf_fsid.psfs_id != findStat.st_dev ||
                    status[i].pst_text.psf_fsid.psfs_type != findStat.st_fstype
                    ))
            {
                continue;
            }
 
            //This is a match
            if(foundCount < fillCount)
                pid[foundCount] = status[i].pst_pid;
            foundCount++;
        }
        //Continue looking at the process list where we left off
        inBuffer = pstat_getproc(status, sizeof(status[0]),
                sizeof(status)/sizeof(status[0]),
                status[inBuffer - 1].pst_idx + 1);
        if(inBuffer < 0)
            GCE(ceError = LwMapErrnoToLwError(errno));
    }
    ceError = ERROR_SUCCESS;
#endif

#ifdef HAVE_STRUCT_PSINFO
    if ((dir = opendir("/proc")) == NULL) {
        GCE(ceError = LwMapErrnoToLwError(errno));
    }

    while(1)
    {
        errno = 0;
        dirEntry = readdir(dir);
        if(dirEntry == NULL)
        {
            if(errno != 0)
                GCE(ceError = LwMapErrnoToLwError(errno));
            else
            {
                //No error here. We simply read the last entry
                break;
            }
        }
        if(dirEntry->d_name[0] == '.')
            continue;
        // On AIX, there is a /proc/sys which does not contain a psinfo
        if(!isdigit((int)dirEntry->d_name[0]))
            continue;
        CT_SAFE_FREE_STRING(filePath);
        GCE(ceError = CTAllocateStringPrintf(&filePath, "/proc/%s/psinfo",
                    dirEntry->d_name));
        GCE(ceError = CTCheckFileOrLinkExists(filePath, &bFileExists));
        if(!bFileExists)
        {
            // On AIX 6.1, a defunct process can lack a psinfo file.
            continue;
        }
        GCE(ceError = CTSafeCloseFile(&infoFile));
        GCE(ceError = CTOpenFile(filePath, "r", &infoFile));
        if(fread(&infoStruct, sizeof(infoStruct), 1, infoFile) != 1)
        {
            GCE(ceError = LwMapErrnoToLwError(errno));
        }

        if (owner != (uid_t)-1 && owner != infoStruct.pr_euid)
        {
            continue;
        }
        if (programName != NULL && strcmp(infoStruct.pr_fname, programName))
        {
            continue;
        }
        if (cmdLine != NULL && strcmp(infoStruct.pr_psargs, cmdLine))
        {
            continue;
        }
        if(programFilename != NULL)
        {
            CT_SAFE_FREE_STRING(filePath);
            GCE(ceError = CTAllocateStringPrintf(&filePath,
                        "/proc/%s/object/a.out",
                        dirEntry->d_name));

            while(stat(filePath, &compareStat) < 0)
            {
                if(errno == EINTR)
                    continue;
                if(errno == ENOENT || errno == ENOTDIR)
                {
                    //This process wasn't executed from a file?
                    goto not_match;
                }
                GCE(ceError = LwMapErrnoToLwError(errno));
            }
            if(findStat.st_ino != compareStat.st_ino)
                continue;
            if(findStat.st_dev != compareStat.st_dev)
                continue;
            if(findStat.st_rdev != compareStat.st_rdev)
                continue;
        }
 
        //This is a match
        if(foundCount < fillCount)
            pid[foundCount] = infoStruct.pr_pid;
        foundCount++;
not_match:
        ;
    }
#endif

#if defined(HAVE_KVM_GETPROCS) && HAVE_DECL_KERN_PROC_PATHNAME
    kd = kvm_open(NULL, "/dev/null", NULL, O_RDONLY, NULL);
    if (kd == NULL)
        GCE(ceError = DWORD_ACCESS_DENIED);

    procs = kvm_getprocs(kd, KERN_PROC_PROC, 0, &unfilteredCount);
    if (procs == NULL)
        GCE(ceError = DWORD_ACCESS_DENIED);

    pos = procs;
    for(i = 0; i < unfilteredCount; i++,
        pos = (struct kinfo_proc *)((char *)pos + pos->ki_structsize))
    {
        if (owner != (uid_t)-1 && owner != pos->ki_uid)
        {
            continue;
        }
        if (programName != NULL && strcmp(pos->ki_comm, programName))
        {
            continue;
        }
        if (cmdLine != NULL)
        {
            char **args = kvm_getargv(kd, pos, 0);
            char **argPos = args;
            PCSTR cmdLinePos = cmdLine;

            while (*cmdLinePos != '\0')
            {
                if (argPos == NULL || *argPos == NULL)
                    break;

                if (strncmp(cmdLinePos, *argPos, strlen(*argPos)))
                    break;

                cmdLinePos += strlen(*argPos);
                argPos++;

                if(cmdLinePos[0] == ' ')
                    cmdLinePos++;
            }

            if(*cmdLinePos != '\0' || (argPos != NULL && *argPos != NULL))
            {
                //not a match
                continue;
            }
        }
        if (programFilename != NULL)
        {
            pathBuffer[0] = '\0';
            if (pos->ki_textvp != NULL)
            {
                sysctlName[3] = pos->ki_pid;
                len = sizeof(pathBuffer);
                if( sysctl(sysctlName, 4, pathBuffer, &len, NULL, 0) < 0)
                {
		    /* If the executable path does not exist
		       (e.g. because the file was deleted after
		       the program started), move on */
		    if (errno == ENOENT)
			continue;
                    GCE(ceError = LwMapErrnoToLwError(errno));
                }
            }
            if(strcmp(programFilename, pathBuffer))
                continue;
        }

        //This is a match
        if(foundCount < fillCount)
            pid[foundCount] = pos->ki_pid;
        foundCount++;
    }
    ceError = ERROR_SUCCESS;
#endif

    if(count)
        *count = foundCount;
    else if(!ceError && foundCount == 0)
        ceError = ERROR_PROC_NOT_FOUND;

cleanup:
#ifdef HAVE_STRUCT_PSINFO
    if(dir != NULL)
        closedir(dir);
    CT_SAFE_FREE_STRING(filePath);
    CTSafeCloseFile(&infoFile);
#endif
#if defined(HAVE_KVM_GETPROCS) && HAVE_DECL_KERN_PROC_PATHNAME
    if(kd != NULL)
    {
        kvm_close(kd);
    }
#endif

    return ceError;
}
Пример #17
0
DWORD
ReadNsswitchConf(NsswitchConf *conf, const char *testPrefix,
        BOOLEAN allowFileCreate)
{
    PSTR copyDestPath = NULL;
    PSTR defaultFilePath = NULL;
    DWORD ceError = ERROR_SUCCESS;
    BOOLEAN bFileExists = FALSE;
    memset(conf, 0, sizeof(*conf));
    
    //Keep trying to read different filenames until one of them is found
    if(!bFileExists)
    {
        bFileExists = TRUE;
        ceError = ReadNsswitchFile(conf, testPrefix, "/etc/nsswitch.conf");
        if(ceError == ERROR_FILE_NOT_FOUND)
        {
            bFileExists = FALSE;
            ceError = ERROR_SUCCESS;
        }
        GCE(ceError);
    }

    if(!bFileExists)
    {
        bFileExists = TRUE;
        ceError = ReadNsswitchFile(conf, testPrefix, "/etc/netsvc.conf");
        if(ceError == ERROR_FILE_NOT_FOUND)
        {
            bFileExists = FALSE;
            ceError = ERROR_SUCCESS;
        }
        GCE(ceError);
    }

    /* HP-UX 11.xx does not appear to have an nsswitch file in
       place by default. If we don't find on already installed,
       use our own */

    if(!bFileExists)
    {
        GCE(ceError = CTAllocateStringPrintf(
          &defaultFilePath, "%s%s", testPrefix, NSSWITCH_LWIDEFAULTS));
        GCE(ceError = CTCheckFileExists(defaultFilePath, &bFileExists));
      
        if (bFileExists) {
            ceError = ReadNsswitchFile(conf, testPrefix, NSSWITCH_LWIDEFAULTS);
            GCE(ceError);
            CT_SAFE_FREE_STRING(conf->filename);
            conf->modified = TRUE;

            if(allowFileCreate)
            {
                GCE(ceError = CTStrdup(NSSWITCH_CONF_PATH, &conf->filename));

                /* Copy over the original file. This way the user can more
                 * clearly see what we changed by comparing nsswitch.conf with
                 * nsswitch.conf.lwidentity.orig. Also, the permissions will be
                 * correct this way when the file is written out.
                 */
                DJ_LOG_INFO("Copying default nsswitch file");
                GCE(ceError = CTAllocateStringPrintf(
                    &copyDestPath, "%s%s", testPrefix, NSSWITCH_CONF_PATH));
                ceError = CTCopyFileWithOriginalPerms(defaultFilePath, copyDestPath);
                GCE(ceError);
            }
            else
            {
                GCE(ceError = CTStrdup(NSSWITCH_LWIDEFAULTS, &conf->filename));
            }
        }
    }

    if(!bFileExists)
    {
        GCE(ceError = ERROR_FILE_NOT_FOUND);
    }

cleanup:
    CT_SAFE_FREE_STRING(copyDestPath);
    CT_SAFE_FREE_STRING(defaultFilePath);

    return ceError;
}
Пример #18
0
static PSTR GetNsswitchDescription(const JoinProcessOptions *options, LWException **exc)
{
    PSTR ret = NULL;
    PCSTR configureSteps;
    BOOLEAN hasBadSeLinux;
    QueryResult compatResult = FullyConfigured;
    PSTR compatDescription = NULL;
    NsswitchConf conf;
    DWORD ceError = ERROR_SUCCESS;

    memset(&conf, 0, sizeof(conf));

    LW_CLEANUP_CTERR(exc, UnsupportedSeLinuxEnabled(&hasBadSeLinux));
    if(hasBadSeLinux)
    {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&ret,
"Your machine is using an unsupported SeLinux policy. This must be disabled before nsswitch can be modified to allow active directory users. Please run '/usr/sbin/setenforce Permissive' and then re-run this program."));
        goto cleanup;
    }

    ceError = ReadNsswitchConf(&conf, "", TRUE);
    if(ceError == ERROR_FILE_NOT_FOUND)
    {
        ceError = ERROR_SUCCESS;
        if (options->warningCallback != NULL)
        {
            options->warningCallback(options, "Could not find file", "Could not find nsswitch file");
        }
        goto cleanup;
    }
    LW_CLEANUP_CTERR(exc, ceError);

    if(options->joiningDomain)
    {
        LW_TRY(exc, compatResult = RemoveCompat(&conf, &compatDescription, &LW_EXC));
    }
    if(compatResult == FullyConfigured)
    {
        CT_SAFE_FREE_STRING(compatDescription);
        LW_CLEANUP_CTERR(exc, CTStrdup("", &compatDescription));
    }

    if (options->joiningDomain)
    {
        uid_t uid = 0;
        gid_t gid = 0;
        mode_t mode = 0;
        LW_CLEANUP_CTERR(exc, CTGetOwnerAndPermissions(
            conf.filename, &uid, &gid, &mode));

        if ((mode & 0444) != 0444)
        {
            LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&ret,
"The permissions of 0%03o on %s are invalid. All users must have at least read permission for the file. You can fix this by running 'chmod a+r %s'.", (int)(mode&0777), conf.filename, conf.filename));
            goto cleanup;
        }
    }

    conf.modified = FALSE;
    LW_CLEANUP_CTERR(exc, UpdateNsswitchConf(&conf, options->joiningDomain));

    if(options->joiningDomain && conf.modified)
        configureSteps = 
"The following steps are required and can be performed automatically:\n"
"\t* Edit nsswitch apparmor profile to allow libraries in the " PREFIXDIR "/lib  and " PREFIXDIR "/lib64 directories\n"
"\t* List lwidentity module in /usr/lib/security/methods.cfg (AIX only)\n"
"\t* Add lwidentity to passwd and group/groups line /etc/nsswitch.conf or /etc/netsvc.conf\n";
    else if(conf.modified)
        configureSteps = 
"The following steps are required and can be performed automatically:\n"
"\t* Remove lwidentity module from /usr/lib/security/methods.cfg (AIX only)\n"
"\t* Remove lwidentity from passwd and group/groups line /etc/nsswitch.conf or /etc/netsvc.conf\n"
"The following step is optional:\n"
"\t* Remove apparmor exception for likewise nsswitch libraries\n";
    else
        configureSteps = "";

    if(strlen(compatDescription) || strlen(configureSteps))
    {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&ret,
"%s%sIf any changes are performed, then the following services must be restarted:\n"
"\t* GDM\n"
"\t* XDM\n"
"\t* Cron\n"
"\t* Dbus\n"
"\t* Nscd", compatDescription, configureSteps));
    }
    else
        LW_CLEANUP_CTERR(exc, CTStrdup("Fully Configured", &ret));

cleanup:
    CT_SAFE_FREE_STRING(compatDescription);
    FreeNsswitchConfContents(&conf);
    return ret;
}
Пример #19
0
//Solaris and AIX should have this
DWORD
CTGetPidOfCmdLine(
    PCSTR programName,
    PCSTR programFilename,
    PCSTR cmdLine,
    uid_t owner,
    pid_t *pid,
    size_t *count
    )
{
    DWORD ceError = ERROR_NOT_SUPPORTED;
    size_t fillCount = 0;
    size_t foundCount = 0;
    struct stat findStat;
    DIR *dir = NULL;
    struct dirent *dirEntry = NULL;
    PSTR filePath = NULL;
    struct psinfo infoStruct;
    FILE *infoFile = NULL;
    struct stat compareStat;
    BOOLEAN bFileExists;
#if defined(__LWI_SOLARIS__)
    int (*getzoneid)() = NULL;
    int zoneid = -1;
#endif

    if(count)
    {
        fillCount = *count;
        *count = 0;
    }
    else if(pid != NULL)
        fillCount = 1;

    if(programFilename != NULL)
    {
        while(stat(programFilename, &findStat) < 0)
        {
            if(errno == EINTR)
                continue;
            GCE(ceError = LwMapErrnoToLwError(errno));
        }
    }

    if ((dir = opendir("/proc")) == NULL) {
        GCE(ceError = LwMapErrnoToLwError(errno));
    }

#if defined(__LWI_SOLARIS__)
    getzoneid = dlsym(RTLD_DEFAULT, "getzoneid");
    if (getzoneid)
    {
        zoneid = getzoneid();
    }
#endif

    while(1)
    {
        errno = 0;
        dirEntry = readdir(dir);
        if(dirEntry == NULL)
        {
            if(errno != 0)
                GCE(ceError = LwMapErrnoToLwError(errno));
            else
            {
                //No error here. We simply read the last entry
                break;
            }
        }
        if(dirEntry->d_name[0] == '.')
            continue;
        // On AIX, there is a /proc/sys which does not contain a psinfo
        if(!isdigit((int)dirEntry->d_name[0]))
            continue;
        CT_SAFE_FREE_STRING(filePath);
        GCE(ceError = CTAllocateStringPrintf(&filePath, "/proc/%s/psinfo",
                    dirEntry->d_name));
        GCE(ceError = CTCheckFileOrLinkExists(filePath, &bFileExists));
        if(!bFileExists)
        {
            // On AIX 6.1, a defunct process can lack a psinfo file.
            continue;
        }
        GCE(ceError = CTSafeCloseFile(&infoFile));
        GCE(ceError = CTOpenFile(filePath, "r", &infoFile));
        if(fread(&infoStruct, sizeof(infoStruct), 1, infoFile) != 1)
        {
            GCE(ceError = LwMapErrnoToLwError(errno));
        }

#if defined(__LWI_SOLARIS__)
        if (zoneid != -1)
        {
            int processzoneid = -1;

#ifdef HAVE_STRUCT_PSINFO_PR_ZONEID
            processzoneid = (int) infoStruct.pr_zoneid;
#else
            processzoneid = (int)
                *(infoStruct.pr_filler +
                sizeof(infoStruct.pr_filler)/sizeof(infoStruct.pr_filler[0]) -
                3);
#endif
            if (zoneid != processzoneid)
            {
                continue;
            }
        }
#endif

        if (owner != (uid_t)-1 && owner != infoStruct.pr_euid)
        {
            continue;
        }
        if (programName != NULL && strcmp(infoStruct.pr_fname, programName))
        {
            continue;
        }
        if (cmdLine != NULL && strcmp(infoStruct.pr_psargs, cmdLine))
        {
            continue;
        }
        if(programFilename != NULL)
        {
            CT_SAFE_FREE_STRING(filePath);
            GCE(ceError = CTAllocateStringPrintf(&filePath,
                        "/proc/%s/object/a.out",
                        dirEntry->d_name));

            while(stat(filePath, &compareStat) < 0)
            {
                if(errno == EINTR)
                    continue;
                if(errno == ENOENT || errno == ENOTDIR)
                {
                    //This process wasn't executed from a file?
                    goto not_match;
                }
                GCE(ceError = LwMapErrnoToLwError(errno));
            }
            if(findStat.st_ino != compareStat.st_ino)
                continue;
            if(findStat.st_dev != compareStat.st_dev)
                continue;
            if(findStat.st_rdev != compareStat.st_rdev)
                continue;
        }
 
        //This is a match
        if(foundCount < fillCount)
            pid[foundCount] = infoStruct.pr_pid;
        foundCount++;
not_match:
        ;
    }

    if(count)
        *count = foundCount;
    else if(!ceError && foundCount == 0)
        ceError = ERROR_PROC_NOT_FOUND;

cleanup:
    if(dir != NULL)
        closedir(dir);
    CT_SAFE_FREE_STRING(filePath);
    CTSafeCloseFile(&infoFile);

    return ceError;
}
Пример #20
0
static QueryResult UpdateSshdConf(struct SshConf *conf, PCSTR testPrefix,
        PCSTR binaryPath, BOOLEAN enable, PSTR *changeDescription,
        const JoinProcessOptions *options, LWException **exc)
{
    size_t i;
    BOOLEAN modified = conf->modified;
    PSTR requiredOptions = NULL;
    PSTR optionalOptions = NULL;
    BOOLEAN supported;
    PSTR temp = NULL;
    QueryResult result = NotConfigured;
    const char *requiredSshdOptions[] = {
        "ChallengeResponseAuthentication",
        "UsePAM", "PAMAuthenticationViaKBDInt",
        "KbdInteractiveAuthentication", NULL};
    const char *optionalSshdOptions[] = {"GSSAPIAuthentication",
        "GSSAPICleanupCredentials", NULL};
    SshdVersion version;
    BOOLEAN compromisedOptions = FALSE;

    if(changeDescription != NULL)
        *changeDescription = NULL;

    LW_TRY(exc, GetSshVersion("", &version, binaryPath, &LW_EXC));

    if(enable)
    {
        LW_CLEANUP_CTERR(exc, CTStrdup("", &requiredOptions));
        LW_CLEANUP_CTERR(exc, CTStrdup("", &optionalOptions));
        for(i = 0; requiredSshdOptions[i] != NULL; i++)
        {
            PCSTR option = requiredSshdOptions[i];
            LW_TRY(exc, supported = TestOption(testPrefix, conf, binaryPath, "-t", option, &LW_EXC));
            if(supported)
            {
                PCSTR value = "yes";

                if(IsNewerThanOrEq(&version, 2, 3, 1, -1) &&
                        IsOlderThanOrEq(&version, 3, 3, -1, -1) &&
                        (!strcmp(option, "ChallengeResponseAuthentication") ||
                         !strcmp(option, "PAMAuthenticationViaKBDInt")))
                {
                    value = "no";
                    compromisedOptions = TRUE;
                }
                conf->modified = FALSE;
                LW_CLEANUP_CTERR(exc, SetOption(conf, option, value));
                if(conf->modified)
                {
                    modified = TRUE;
                    temp = requiredOptions;
                    requiredOptions = NULL;
                    CTAllocateStringPrintf(&requiredOptions, "%s\t%s\n", temp, option);
                    CT_SAFE_FREE_STRING(temp);
                }
            }
        }
        for(i = 0; optionalSshdOptions[i] != NULL; i++)
        {
            PCSTR option = optionalSshdOptions[i];
            LW_TRY(exc, supported = TestOption(testPrefix, conf, binaryPath, "-t", option, &LW_EXC));
            if(supported)
            {
                conf->modified = FALSE;
                LW_CLEANUP_CTERR(exc, SetOption(conf, option, "yes"));
                if(conf->modified)
                {
                    modified = TRUE;
                    temp = optionalOptions;
                    optionalOptions = NULL;
                    LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&optionalOptions, "%s\t%s\n", temp, option));
                    CT_SAFE_FREE_STRING(temp);
                }
            }
        }
        result = FullyConfigured;
        if(strlen(optionalOptions) > 0)
        {
            temp = optionalOptions;
            optionalOptions = NULL;
            LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(
                        &optionalOptions,
                        "The following options will be enabled in %s to provide a better user experience:\n%s",
                        conf->filename, temp));
            CT_SAFE_FREE_STRING(temp);
            result = SufficientlyConfigured;
        }
        if(strlen(requiredOptions) > 0)
        {
            temp = requiredOptions;
            requiredOptions = NULL;
            LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(
                        &requiredOptions,
                        "The following options must be enabled in %s:\n%s",
                        conf->filename, temp));
            CT_SAFE_FREE_STRING(temp);
            result = NotConfigured;
        }
        if(changeDescription != NULL)
        {
            LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(
                        changeDescription,
                        "%s%s",
                        requiredOptions, optionalOptions));
        }
    }
    else
    {
        conf->modified = FALSE;
        LW_CLEANUP_CTERR(exc, RemoveOption(conf, "GSSAPIAuthentication"));
        if(conf->modified)
        {
            if(changeDescription != NULL)
            {
                LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(
                            changeDescription,
                            "In %s, GSSAPIAuthentication may optionally be removed.\n",
                            conf->filename));
            }
            result = SufficientlyConfigured;
        }
        else
            result = FullyConfigured;
    }
    if(changeDescription != NULL && *changeDescription == NULL)
        LW_CLEANUP_CTERR(exc, CTStrdup("", changeDescription));

    if(options != NULL)
    {
        ModuleState *state = DJGetModuleStateByName(options, "ssh");
        if(state->moduleData == (void *)-1)
        {
            //We already showed a warning
        }
        else if(compromisedOptions)
        {
            state->moduleData = (void *)-1;
            if (options->warningCallback != NULL)
            {
                options->warningCallback(options, "Unpatched version of SSH",
                        "The version of your sshd daemon indicates that it is susceptible to the remote exploit described at http://www.openssh.com/txt/preauth.adv . To avoid exposing your system to this exploit, the 'ChallengeResponseAuthentication' and 'PAMAuthenticationViaKBDInt' options will be set to 'no' instead of 'yes'. As a side effect, all login error messages will appear as 'permission denied' instead of more detailed messages like 'account disabled'. Additionally, password changes on login may not work.\n\
\n\
Even when those options are disabled, your system is still vulnerable to http://www.cert.org/advisories/CA-2003-24.html . We recommend upgrading your version of SSH.");
            }
        }
Пример #21
0
static void ConfigureApparmor(BOOLEAN enable, LWException **exc)
{
    DWORD ceError = ERROR_SUCCESS;
    BOOLEAN hasApparmor;
    BOOLEAN configured;
    BOOLEAN usingMr;
    FILE *file = NULL;
    PCSTR addString;
    PSTR restartPath = NULL;
    PSTR restartCommand = NULL;
    char *tempName = NULL;
    char *finalName = NULL;

    LW_CLEANUP_CTERR(exc, IsApparmorConfigured(&configured));
    if(configured == enable)
        goto cleanup;

    LW_CLEANUP_CTERR(exc, CTCheckFileOrLinkExists(APPARMOR_NSSWITCH,
                &hasApparmor));
    if(!hasApparmor)
        goto cleanup;

    GCE(ceError = CTGetFileTempPath(
                        APPARMOR_NSSWITCH,
                        &finalName,
                        &tempName));

    LW_CLEANUP_CTERR(exc, CTCheckFileHoldsPattern(finalName,
                "mr,", &usingMr));

    if(usingMr)
        addString = 
PREFIXDIR "/lib/*.so*            mr,\n"
PREFIXDIR "/lib64/*.so*          mr,\n"
"/tmp/.lwidentity/pipe              rw,\n"
LOCALSTATEDIR "/lib/likewise/.lsassd  rw,\n"
LOCALSTATEDIR "/tmp/.lsaclient_*              rw,\n";
    else
        addString =
PREFIXDIR "/lib/*.so*            r,\n"
PREFIXDIR "/lib64/*.so*          r,\n"
"/tmp/.lwidentity/pipe              rw,\n"
LOCALSTATEDIR "/lib/likewise/.lsassd  rw,\n"
LOCALSTATEDIR "/tmp/.lsaclient_*              rw,\n";


    if(enable)
    {
        LW_CLEANUP_CTERR(exc, CTCopyFileWithOriginalPerms(finalName, tempName));
        LW_CLEANUP_CTERR(exc, CTOpenFile(tempName, "a", &file));
        LW_CLEANUP_CTERR(exc, CTFilePrintf(file, "# likewise\n%s# end likewise\n",
                    addString));

        CTSafeCloseFile(&file);

        LW_CLEANUP_CTERR(exc, CTSafeReplaceFile(finalName, tempName));
    }
    else
    {
        LW_CLEANUP_CTERR(exc, CTRunSedOnFile(finalName, finalName, FALSE, "/^[ \t]*#[ \t]*likewise[ \t]*$/,/^[ \t]*#[ \t]*end likewise[ \t]*$/d"));
        LW_CLEANUP_CTERR(exc, CTRunSedOnFile(finalName, finalName, FALSE, "/^[ \t]*#[ \t]*centeris[ \t]*$/,/^[ \t]*#[ \t]*end centeris[ \t]*$/d"));
    }


    ceError = CTFindFileInPath("rcapparmor", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", &restartPath);
    if(ceError == ERROR_FILE_NOT_FOUND)
    {
        ceError = CTFindFileInPath("apparmor", "/etc/init.d/apparmor", &restartPath);
    }
    
    if(ceError == ERROR_FILE_NOT_FOUND)
    {
        ceError = ERROR_SUCCESS;
    }
    else if(!ceError)
    {
        LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&restartCommand,
                    "%s restart", restartPath));
        LW_TRY(exc, CTCaptureOutputToExc(restartCommand, &LW_EXC));
    }
    LW_CLEANUP_CTERR(exc, ceError);

cleanup:
    if(file != NULL)
    {
        CTCloseFile(file);
        CTRemoveFile(tempName);
    }
    CT_SAFE_FREE_STRING(restartPath);
    CT_SAFE_FREE_STRING(restartCommand);
    CT_SAFE_FREE_STRING(tempName);
    CT_SAFE_FREE_STRING(finalName);
}
Пример #22
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;
}