读取内存时访问冲突

时间:2015-05-19 23:08:51

标签: c++ windows memory active-directory ldap

背景

我正在使用Visual Studio 2013 Community Edition编写命令行C ++程序。它通过LDAP连接到Active Directory服务器,并检索几个属性中的唯一值列表(例如:办公室位置,部门)。

问题

程序编译得很好,但是当我运行它时会遇到内存访问问题:

Unhandled exception at 0x74EDC6B1 (Wldap32.dll) in LdapSearchResultTest1.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.

这是我第一次将C ++与外部库一起使用,所以我不确定如何进行调试(通常我会为Android编写Java)。我花了大部分时间来寻找SO并根据类似问题的答案尝试想法,但我仍然无法弄明白。

确切的问题在于此函数调用中的最后一个参数:

// Do the search
int searchReturnCode = ldap_search_s(
    ldapSession,
    &searchBase[0],
    LDAP_SCOPE_SUBTREE,
    filter,
    pAttributes,
    0,
    &pSearchResults); // Error is here

我的代码

我的代码基于MSDN网站上的一个示例,我在我的代码之后复制了该代码。以下是SSCCE来说明问题:

#include<iostream>
#include<Windows.h>
#include<Winldap.h>
#include<WinBer.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

// Function headers
vector<string> get_unique_departments(LDAP*, char*, string);
vector<string> get_unique_office_locations(LDAP*, char*, string);
vector<string> get_unique_values_by_property(LDAP*, char*, string, string);
void print_list(string, vector<string>);

// Main function
int main(int argc, char* argv[]) {

    /**
     * Take AD connection arguments from Windows command line:
     * - Server address
     * - Server port
     * - Username
     * - Password
     * - Search base(s) (space separated if there are multiple)
     *
     * Example call from Windows command line:
     * > program.exe ad-test.example.com 389 joe@ad-test.example.com L3tM31n "OU=Development Team,DC=ad-test,DC=example,DC=com" "OU=Management Team,DC=ad-test,DC=example,DC=com"
     */
    string serverAddress = argv[1];
    int serverPort = atoi(argv[2]);
    string username = argv[3];
    string password = argv[4];
    vector<string> searchBases;
    for (int i = 0; i < argc; i++) {
        searchBases.push_back(argv[i]);
    }

    // If debug build, print received parameters
    #ifdef _DEBUG
    cout << "Server address: " << serverAddress << endl
        << "Server port: " << serverPort << endl
        << "Username: " << username << endl
        << "Password: " << password << endl;
    for (size_t i = 5; i < searchBases.size(); i++) {
        cout << "Search base: " << searchBases.at(i) << endl;
    }
    cout << endl;
    #endif

    // Initiate LDAP connection to Active Directory
    int ldapVersion = LDAP_VERSION3;
    LDAP* ldapSession = ldap_init(&serverAddress[0], serverPort);
    ldap_set_option(ldapSession, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
    ULONG ldapConnection = ldap_connect(ldapSession, nullptr);

    // Bind user
    int ldapBindResult = ldap_simple_bind_s(ldapSession, &username[0], &password[0]);
    if (ldapBindResult != LDAP_SUCCESS) {
        ldap_unbind(ldapSession);
        #ifdef _DEBUG
        cout << "Unable to connect to LDAP directory" << endl << endl;
        #endif
    }
    else {
        #ifdef _DEBUG
        cout << "Connected to LDAP!" << endl << endl;
        #endif
    }

    // The LDAP object filter
    char* filter = "(&(objectCategory=person)(objectClass=user))";

    // Get lists of departments and offices
    vector<string> departments, offices;
    for (int i = 0; i < searchBases.size(); i++) {
        vector<string> tempDepts = get_unique_departments(ldapSession, filter, searchBases.at(i));
        vector<string> tempOffices = get_unique_office_locations(ldapSession, filter, searchBases.at(i));
        for (int j = 0; j < tempDepts.size(); j++) {
            departments.push_back(tempDepts.at(j));
        }
        for (int j = 0; j < tempOffices.size(); j++) {
            offices.push_back(tempOffices.at(j));
        }
    }

    // Print the lists
    print_list("Departments", departments);
    print_list("Offices", offices);

    // Return
    return 0;

}

// Retrieve a list of unique departments
vector<string> get_unique_departments(LDAP* session, char* filter, string searchBase) {
    return get_unique_values_by_property(session, filter, searchBase, "department");
}

// Retrieve a list of unique office locations
vector<string> get_unique_office_locations(LDAP* session, char* filter, string searchBase) {
    return get_unique_values_by_property(session, filter, searchBase, "office");
}

// Get a list of an attribute's unique values
vector<string> get_unique_values_by_property(LDAP* ldapSession, char* filter, string searchBase, string property) {

    // Initialize some variables
    vector<string> results;
    char* pAttributes[1];
    pAttributes[0] = &property[0];
    LDAPMessage* pSearchResults = NULL;
    int numResults = 0;

    // Do the search
    int searchReturnCode = ldap_search_s(
        ldapSession,
        &searchBase[0],
        LDAP_SCOPE_SUBTREE,
        filter,
        pAttributes,
        0,
        &pSearchResults); // Error is here

    // Process results
    if (searchReturnCode == LDAP_SUCCESS) {

        // Initialize some variables
        LDAPMessage* pEntry = NULL;
        char* pEntryDN = NULL;
        char* sMsg;
        BerElement* pBer = NULL;
        char* pAttribute = NULL;
        char** ppValue = NULL;
        ULONG iValue = 0;

        // Count the results
        numResults = ldap_count_entries(ldapSession, pSearchResults);

        // Loop over results
        for (ULONG i = 0; i < numResults; i++) {

            // Get the first/next entry
            if (!i) {
                pEntry = ldap_first_entry(ldapSession, pSearchResults);
            }
            else {
                pEntry = ldap_next_entry(ldapSession, pEntry);
            }

            // Fail if unable to get entry
            if (pEntry == NULL) {
                results.clear();
                return results;
            }

            // Loop over the attributes
            pAttribute = ldap_first_attribute(ldapSession, pEntry, &pBer);
            while (pAttribute != NULL) {

                // Get and handle the values
                ppValue = ldap_get_values(ldapSession, pEntry, pAttribute);
                if (ppValue != NULL) {
                    iValue = ldap_count_values(ppValue);
                    if (find(results.begin(), results.end(), *ppValue) == results.end()) {
                        results.push_back(*ppValue);
                    }

                    // Memory management
                    ldap_value_free(ppValue);
                    ppValue = NULL;
                    ldap_memfree(pAttribute);

                    // Get the next attribute
                    pAttribute = ldap_next_attribute(ldapSession, pEntry, pBer);
                }

            }

            // Memory management
            if (pBer != NULL) {
                ber_free(pBer, 0);
            }
            pBer = NULL;

        }

    }

    // Free search result memory
    if (pSearchResults != NULL) {
        ldap_msgfree(pSearchResults);
    }

    // Return
    return results;

}

// Print a vector-based list w/ header
void print_list(string header, vector<string> items) {
    if (items.size() > 0) {
        cout << header << ":" << endl;
        for (int i = 0; i < items.size(); i++) {
            cout << items.at(i) << endl;
        }
        cout << endl;
    }
}

MSDN示例:搜索LDAP目录

来自https://msdn.microsoft.com/en-us/library/aa367016%28v=vs.85%29.aspx

//----------------------------------------------
// Performing an LDAP Synchronous Search.
//
// Be aware that you must set the command prompt screen buffer 
// height to 350 and the width to 90.
//-------------------------------------------------------------

#include <windows.h>
#include <winldap.h>
#include <winber.h>
#include <rpc.h>
#include <rpcdce.h>
#include <stdio.h>

//-----------------------------------------------------------
// This subroutine must have validated credentials (name and
// password) passed to it.
//-----------------------------------------------------------
int MyLDAPSearch(PCHAR pUserName, PCHAR pPassword)
{
    //---------------------------------------------------------
    // Initialize a session. LDAP_PORT is the default port, 389.
    //---------------------------------------------------------
    PCHAR hostName = "fabrikam.com";
    LDAP* pLdapConnection = NULL;

    pLdapConnection = ldap_init(hostName, LDAP_PORT);

    if (pLdapConnection == NULL)
    {
        printf("ldap_init failed with 0x%x.\n",LdapGetLastError());
        ldap_unbind(pLdapConnection);
        return -1;
    }
    else
        printf("ldap_init succeeded \n");


    //-------------------------------------------------------
    // Set session options.
    //-------------------------------------------------------
    ULONG version = LDAP_VERSION3;
    ULONG numReturns = 10;
    ULONG lRtn = 0;

    // Set the version to 3.0 (default is 2.0).
    lRtn = ldap_set_option(
                    pLdapConnection,           // Session handle
                    LDAP_OPT_PROTOCOL_VERSION, // Option
                    (void*) &version);         // Option value

    if(lRtn == LDAP_SUCCESS)
        printf("ldap version set to 3.0 \n");
    else
    {
        printf("SetOption Error:%0lX\n", lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }

    // Set the limit on the number of entries returned to 10.
    lRtn = ldap_set_option(
                    pLdapConnection,       // Session handle
                    LDAP_OPT_SIZELIMIT,    // Option
                    (void*) &numReturns);  // Option value

    if(lRtn == LDAP_SUCCESS)
        printf("Max return entries set to 10 \n");
    else
    {
        printf("SetOption Error:%0lX\n", lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }


    //--------------------------------------------------------
    // Connect to the server.
    //--------------------------------------------------------

    lRtn = ldap_connect(pLdapConnection, NULL);

    if(lRtn == LDAP_SUCCESS)
        printf("ldap_connect succeeded \n");
    else
    {
        printf("ldap_connect failed with 0x%lx.\n",lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }


    //--------------------------------------------------------
    // Bind with credentials.
    //--------------------------------------------------------
    PCHAR pMyDN = "DC=fabrikam,DC=com";
    SEC_WINNT_AUTH_IDENTITY secIdent;

    secIdent.User = (unsigned char*)pUserName;
    secIdent.UserLength = strlen(pUserName);
    secIdent.Password = (unsigned char*)pPassword;
    secIdent.PasswordLength = strlen(pPassword);
    secIdent.Domain = (unsigned char*)hostName;
    secIdent.DomainLength = strlen(hostName);
    secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;

    lRtn = ldap_bind_s(
                pLdapConnection,      // Session Handle
                pMyDN,                // Domain DN
                (PCHAR)&secIdent,     // Credential structure
                LDAP_AUTH_NEGOTIATE); // Auth mode
    if(lRtn == LDAP_SUCCESS)
    {
        printf("ldap_bind_s succeeded \n");
        secIdent.Password = NULL; // Remove password pointer
        pPassword = NULL;         // Remove password pointer
    }
    else
    {
        printf("ldap_bind_s failed with 0x%lx.\n",lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }

    //----------------------------------------------------------
    // Perform a synchronous search of fabrikam.com for 
    // all user objects that have a "person" category.
    //----------------------------------------------------------
    ULONG errorCode = LDAP_SUCCESS;
    LDAPMessage* pSearchResult;
    PCHAR pMyFilter = "(&(objectCategory=person)(objectClass=user))";
    PCHAR pMyAttributes[6];

    pMyAttributes[0] = "cn";
    pMyAttributes[1] = "company";
    pMyAttributes[2] = "department";
    pMyAttributes[3] = "telephoneNumber";
    pMyAttributes[4] = "memberOf";
    pMyAttributes[5] = NULL;

    errorCode = ldap_search_s(
                    pLdapConnection,    // Session handle
                    pMyDN,              // DN to start search
                    LDAP_SCOPE_SUBTREE, // Scope
                    pMyFilter,          // Filter
                    pMyAttributes,      // Retrieve list of attributes
                    0,                  // Get both attributes and values
                    &pSearchResult);    // [out] Search results

    if (errorCode != LDAP_SUCCESS)
    {
        printf("ldap_search_s failed with 0x%0lx \n",errorCode);
        ldap_unbind_s(pLdapConnection);
        if(pSearchResult != NULL)
            ldap_msgfree(pSearchResult);
        return -1;
    }
    else
        printf("ldap_search succeeded \n");

    //----------------------------------------------------------
    // Get the number of entries returned.
    //----------------------------------------------------------
    ULONG numberOfEntries;

    numberOfEntries = ldap_count_entries(
                        pLdapConnection,    // Session handle
                        pSearchResult);     // Search result

    if(numberOfEntries == NULL)
    {
        printf("ldap_count_entries failed with 0x%0lx \n",errorCode);
        ldap_unbind_s(pLdapConnection);
        if(pSearchResult != NULL)
            ldap_msgfree(pSearchResult);
        return -1;
    }
    else
        printf("ldap_count_entries succeeded \n");

    printf("The number of entries is: %d \n", numberOfEntries);


    //----------------------------------------------------------
    // Loop through the search entries, get, and output the
    // requested list of attributes and values.
    //----------------------------------------------------------
    LDAPMessage* pEntry = NULL;
    PCHAR pEntryDN = NULL;
    ULONG iCnt = 0;
    char* sMsg;
    BerElement* pBer = NULL;
    PCHAR pAttribute = NULL;
    PCHAR* ppValue = NULL;
    ULONG iValue = 0;

    for( iCnt=0; iCnt < numberOfEntries; iCnt++ )
    {
        // Get the first/next entry.
        if( !iCnt )
            pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
        else
            pEntry = ldap_next_entry(pLdapConnection, pEntry);

        // Output a status message.
        sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
        if( pEntry == NULL )
        {
            printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
            ldap_unbind_s(pLdapConnection);
            ldap_msgfree(pSearchResult);
            return -1;
        }
        else
            printf("%s succeeded\n",sMsg);

        // Output the entry number.
        printf("ENTRY NUMBER %i \n", iCnt);

        // Get the first attribute name.
        pAttribute = ldap_first_attribute(
                      pLdapConnection,   // Session handle
                      pEntry,            // Current entry
                      &pBer);            // [out] Current BerElement

        // Output the attribute names for the current object
        // and output values.
        while(pAttribute != NULL)
        {
            // Output the attribute name.
            printf("     ATTR: %s",pAttribute);

            // Get the string values.

            ppValue = ldap_get_values(
                          pLdapConnection,  // Session Handle
                          pEntry,           // Current entry
                          pAttribute);      // Current attribute

            // Print status if no values are returned (NULL ptr)
            if(ppValue == NULL)
            {
                printf(": [NO ATTRIBUTE VALUE RETURNED]");
            }

            // Output the attribute values
            else
            {
                iValue = ldap_count_values(ppValue);
                if(!iValue)
                {
                    printf(": [BAD VALUE LIST]");
                }
                else
                {
                    // Output the first attribute value
                    printf(": %s", *ppValue);

                    // Output more values if available
                    ULONG z;
                    for(z=1; z<iValue; z++)
                    {
                        printf(", %s", ppValue[z]);
                    }
                }
            } 

            // Free memory.
            if(ppValue != NULL)  
                ldap_value_free(ppValue);
            ppValue = NULL;
            ldap_memfree(pAttribute);

            // Get next attribute name.
            pAttribute = ldap_next_attribute(
                            pLdapConnection,   // Session Handle
                            pEntry,            // Current entry
                            pBer);             // Current BerElement
            printf("\n");
        }

        if( pBer != NULL )
            ber_free(pBer,0);
        pBer = NULL;
    }

    //----------------------------------------------------------
    // Normal cleanup and exit.
    //----------------------------------------------------------
    ldap_unbind(pLdapConnection);
    ldap_msgfree(pSearchResult);
    ldap_value_free(ppValue);
    return 0;

}

2 个答案:

答案 0 :(得分:2)

阅读ldap_search_s的文档,我看到了:

  

base [in]

     

指向以null结尾的字符串的指针,该字符串包含开始搜索的条目的可分辨名称。

但是,&searchBase[0]只会为您提供指向存储std::string使用的指针 - 不要求此为空终止。您应该使用searchBase.c_str()代替,因为这可以保证为您提供空终止的c字符串。

答案 1 :(得分:1)

EAGER数组必须以空值终止。

代码应如下所示:

pAttributes