//ldapAuth9x.cpp
/////////////////////////////////////////////////////////////////////////////
//  Copyright (C) 2002 Ultr@VNC Team Members. All Rights Reserved.
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//
// If the source code for the program is not available from the place from
// which you received this file, check 
// http://ultravnc.sourceforge.net/
// /macine-vnc Greg Wood (wood@agressiv.com)


#include "ldapauth9x.h"

/////////////////////////

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                                         )
{
    switch (ul_reason_for_call)
        {
                case DLL_PROCESS_ATTACH:
                case DLL_THREAD_ATTACH:
                case DLL_THREAD_DETACH:
                case DLL_PROCESS_DETACH:
                        break;
    }
    return TRUE;
}




LDAPAUTH9X_API
BOOL CUGP(char * userin,char *password,char *machine, char * groupin,int locdom)
{
        OSVERSIONINFO ovi = { sizeof ovi };
        GetVersionEx( &ovi );
        //if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT &&
    //  ovi.dwMajorVersion >= 5 )
        if ( 5 >= 5 )
        {
        //Handle the command line arguments.
        LPOLESTR pszBuffer = new OLECHAR[MAX_PATH*2];
        LPOLESTR pszBuffer2 = new OLECHAR[MAX_PATH*2];
        LPOLESTR pszBuffer3 = new OLECHAR[MAX_PATH*2];
        LPOLESTR pszBuffer4 = new OLECHAR[MAX_PATH*2];
        mbstowcs( (wchar_t *) pszBuffer, userin, MAX_PATH );
        mbstowcs( (wchar_t *) pszBuffer2, password, MAX_PATH );
        mbstowcs( (wchar_t *) pszBuffer3, machine, MAX_PATH );
        mbstowcs( (wchar_t *) pszBuffer4, groupin, MAX_PATH );
        HRESULT hr = S_OK;
        //Get rootDSE and the domain container's DN.
        IADs *pObject = NULL;
        IADs *pObjectUser = NULL;
        IADs *pObjectGroup = NULL;
        IDirectorySearch *pDS = NULL;
        LPOLESTR szPath = new OLECHAR[MAX_PATH];
        LPOLESTR myPath = new OLECHAR[MAX_PATH];
        
        wcscpy(szPath,L"LDAP://dc1.ad.local/dc=ad,dc=local"); //set to root of domain or search path
        wprintf(szPath);
        wprintf(L"\n");
        //VariantClear(&var);
        if (pObject)
                {
                        pObject->Release();
                        pObject = NULL;
                }
        wprintf( szPath);
        wprintf(L"\n");
        //Bind to the root of the current domain.
        hr = ADsOpenObject(szPath,pszBuffer,pszBuffer2,
                         ADS_SECURE_AUTHENTICATION,IID_IDirectorySearch,(void**)&pDS);
        if (SUCCEEDED(hr))
                {
                        if (SUCCEEDED(hr))
                                {
                                        hr =  FindUserByName(pDS, pszBuffer, pszBuffer, pszBuffer2, &pObjectUser);
                                        if (FAILED(hr))
                                                {
                                                        wprintf(L"User not found %i\n",hr);
                                                        delete [] pszBuffer;
                                                        delete [] pszBuffer2;
                                                        delete [] pszBuffer3;
                                                        delete [] szPath;
                                                        delete [] myPath;
                                                        if (pDS) pDS->Release();
                                                        if (pObjectUser) pObjectUser->Release();
                                                        return false;
                                                }
                                        if (pObjectUser) pObjectUser->Release();
                                        ///////////////////// VNCACCESS
                                        hr =  FindGroup(pDS, pszBuffer, pszBuffer, pszBuffer2, &pObjectGroup,pszBuffer4);
                                        if (pObjectGroup)
                                                                {
                                                                        pObjectGroup->Release();
                                                                        pObjectGroup = NULL;
                                                                }
                                        if (FAILED(hr)) wprintf(L"group not found\n");
                                        if (SUCCEEDED(hr))
                                                {
                                                        wprintf(L"Group found OK\n");
                                                        IADsGroup *     pIADsG;
                                                        hr = ADsOpenObject( gbsGroup,pszBuffer, pszBuffer2, 
                                                                        ADS_SECURE_AUTHENTICATION,IID_IADsGroup, (void**) &pIADsG);
                                                        if (SUCCEEDED(hr))
                                                                {
                                                                        VARIANT_BOOL bMember = FALSE;  
                                                                        hr = pIADsG->IsMember(gbsMember,&bMember);
                                                                        if (SUCCEEDED(hr))
                                                                                {
                                                                                        if (bMember == -1)
                                                                                                {
                                                                                                        wprintf(L"Object \n\n%s\n\n IS a member of the following Group:\n\n%s\n\n",gbsMember,gbsGroup);
                                                                                                        delete [] pszBuffer;
                                                                                                        delete [] pszBuffer2;
                                                                                                        delete [] pszBuffer3;
                                                                                                        delete [] szPath;
                                                                                                        delete [] myPath;
                                                                                                        if (pDS) pDS->Release();
                                                                                                        return true;
                                                                                                }
                                                                                        else
                                                                                                {
                                                                                                        BSTR bsMemberGUID = NULL;
                                                                                                        IDirectoryObject * pDOMember = NULL;
                                                                                                        hr = ADsOpenObject( gbsMember,pszBuffer, pszBuffer2, 
                                                                                                                        ADS_SECURE_AUTHENTICATION,IID_IDirectoryObject, (void**) &pDOMember);
                                                                                                        if (SUCCEEDED(hr))
                                                                                                                {
                                                                                                                        hr = GetObjectGuid(pDOMember,bsMemberGUID);
                                                                                                                        pDOMember->Release();
                                                                                                                        pDOMember  = NULL;
                                                                                                                        if (RecursiveIsMember(pIADsG,bsMemberGUID,gbsMember,true, pszBuffer, pszBuffer2))
                                                                                                                                {
                                                                                                                                        delete [] pszBuffer;
                                                                                                                                        delete [] pszBuffer2;
                                                                                                                                        delete [] pszBuffer3;
                                                                                                                                        delete [] szPath;
                                                                                                                                        delete [] myPath;
                                                                                                                                        if (pDS) pDS->Release();
                                                                                                                                        return true;
                                                                                                                                }
                                                                                                                }
                                                                                        }//else bmember
                                                                        }//ismember
                                                        }//iadsgroup 
                                        }//Findgroup
                                        wprintf(L"USER not found in group\n");
                                        
                                }//user
                }
        if (pDS) pDS->Release();

                /*LOGFAILED(pszBuffer3,pszBuffer);*/
                delete [] pszBuffer;
                delete [] pszBuffer2;
                delete [] pszBuffer3;
                delete [] szPath;
                delete [] myPath;
                return false;
        }
        return false;
}

HRESULT FindUserByName(IDirectorySearch *pSearchBase, //Container to search
                                           LPOLESTR szFindUser, LPOLESTR  pwszUser, LPOLESTR pwszPassword, //Name of user to find.
                                           IADs **ppUser) //Return a pointer to the user
{
    HRESULT hrObj = E_FAIL;
    HRESULT hr = E_FAIL;
        if ((!pSearchBase)||(!szFindUser))
                return E_INVALIDARG;
        //Create search filter
        LPOLESTR pszSearchFilter = new OLECHAR[MAX_PATH];
        LPOLESTR szADsPath = new OLECHAR[MAX_PATH];
        wcscpy(pszSearchFilter, L"(&(objectClass=user)(samAccountName=");
        wcscat(pszSearchFilter, szFindUser);
        wcscat(pszSearchFilter, L"))");
    //Search entire subtree from root.
        ADS_SEARCHPREF_INFO SearchPrefs;
        SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
        SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
        SearchPrefs.vValue.Integer = ADS_SCOPE_SUBTREE;
    DWORD dwNumPrefs = 1;
        // COL for iterations
    ADS_SEARCH_COLUMN col;
    // Handle used for searching
    ADS_SEARCH_HANDLE hSearch;
        // Set the search preference
    hr = pSearchBase->SetSearchPreference( &SearchPrefs, dwNumPrefs);
    if (FAILED(hr))
        return hr;
        // Set attributes to return
        CONST DWORD dwAttrNameSize = 1;
    LPOLESTR pszAttribute[dwAttrNameSize] = {L"ADsPath"};

    // Execute the search
        wprintf(pszSearchFilter);
        wprintf(L"\n");
    hr = pSearchBase->ExecuteSearch(pszSearchFilter,
                                          pszAttribute,
                                                                  dwAttrNameSize,
                                                                  &hSearch
                                                                  );
        if (SUCCEEDED(hr))
        {    

    // Call IDirectorySearch::GetNextRow() to retrieve the next row 
    //of data
        while( pSearchBase->GetNextRow( hSearch) != S_ADS_NOMORE_ROWS )
                {
            // loop through the array of passed column names,
            // print the data for each column
            for (DWORD x = 0; x < dwAttrNameSize; x++)
            {
                            // Get the data for this column
                hr = pSearchBase->GetColumn( hSearch, pszAttribute[x], &col );
                            if ( SUCCEEDED(hr) )
                            {
                                    // Print the data for the column and free the column
                                        // Note the attribute we asked for is type CaseIgnoreString.
                    wcscpy(szADsPath, col.pADsValues->CaseIgnoreString); 
                                        hr = ADsOpenObject(szADsPath,
                                                                         pwszUser,
                                                                         pwszPassword,
                                                                         ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
                                                                         IID_IADs,
                                                                         (void**)ppUser);
                                        if (SUCCEEDED(hr))
                                        { 
                       wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); 
                                           hrObj = S_OK;
                                           gbsMember=SysAllocString(col.pADsValues->CaseIgnoreString);
                                        }
                                    pSearchBase->FreeColumn( &col );
                            }
                            else
                                    hr = E_FAIL;
            }
                }
                // Close the search handle to clean up
        pSearchBase->CloseSearchHandle(hSearch);
        }
        if (FAILED(hrObj))
                hr = hrObj;
    return hr;
}

HRESULT FindGroup(IDirectorySearch *pSearchBase, //Container to search
                                           LPOLESTR szFindUser, LPOLESTR  pwszUser, LPOLESTR pwszPassword, //Name of user to find.
                                           IADs **ppUser,LPOLESTR szGroup) //Return a pointer to the user
{
    HRESULT hrObj = E_FAIL;
    HRESULT hr = E_FAIL;
        if ((!pSearchBase)||(!szFindUser))
                return E_INVALIDARG;
        //Create search filter
        LPOLESTR pszSearchFilter = new OLECHAR[MAX_PATH];
        LPOLESTR szADsPath = new OLECHAR[MAX_PATH];
        wcscpy(pszSearchFilter, L"(&(objectClass=group)(cn=");
        wcscat(pszSearchFilter,szGroup);
        wcscat(pszSearchFilter, L"))");
    //Search entire subtree from root.
        ADS_SEARCHPREF_INFO SearchPrefs;
        SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
        SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
        SearchPrefs.vValue.Integer = ADS_SCOPE_SUBTREE;
    DWORD dwNumPrefs = 1;
        // COL for iterations
    ADS_SEARCH_COLUMN col;
    // Handle used for searching
    ADS_SEARCH_HANDLE hSearch;
        // Set the search preference
    hr = pSearchBase->SetSearchPreference( &SearchPrefs, dwNumPrefs);
    if (FAILED(hr))
        return hr;
        // Set attributes to return
        CONST DWORD dwAttrNameSize = 1;
    LPOLESTR pszAttribute[dwAttrNameSize] = {L"ADsPath"};

    // Execute the search
    hr = pSearchBase->ExecuteSearch(pszSearchFilter,
                                          pszAttribute,
                                                                  dwAttrNameSize,
                                                                  &hSearch
                                                                  );
        if (SUCCEEDED(hr))
        {    

    // Call IDirectorySearch::GetNextRow() to retrieve the next row 
    //of data
        while( pSearchBase->GetNextRow( hSearch) != S_ADS_NOMORE_ROWS )
                {
            // loop through the array of passed column names,
            // print the data for each column
            for (DWORD x = 0; x < dwAttrNameSize; x++)
            {
                            // Get the data for this column
                hr = pSearchBase->GetColumn( hSearch, pszAttribute[x], &col );
                            if ( SUCCEEDED(hr) )
                            {
                                    // Print the data for the column and free the column
                                        // Note the attribute we asked for is type CaseIgnoreString.
                    wcscpy(szADsPath, col.pADsValues->CaseIgnoreString); 
                                        hr = ADsOpenObject(szADsPath,
                                                                         pwszUser,
                                                                         pwszPassword,
                                                                         ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
                                                                         IID_IADs,
                                                                         (void**)ppUser);
                                        if (SUCCEEDED(hr))
                                        { 
                       wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); 
                                           hrObj = S_OK;
                                           gbsGroup=SysAllocString(col.pADsValues->CaseIgnoreString);
                                        }
                                    pSearchBase->FreeColumn( &col );
                            }
                            else
                                    hr = E_FAIL;
            }
                }
                // Close the search handle to clean up
        pSearchBase->CloseSearchHandle(hSearch);
        }
        if (FAILED(hrObj))
                hr = hrObj;
    return hr;
}




////////////////////////////////////////////////////////////////////////////////////////////////////
/*  RecursiveIsMember()                       - Recursively scans the members of passed IADsGroup ptr
                                                and any groups that belong to the passed ptr- for membership
                                                of Passed group.
                                                Will return a TRUE if the member is found in the passed group,
                                                or if the passed member is a member of any group which is a member
                                                of the passed group.
    Parameters
 
        IADsGroup *     pADsGroup       - Group from which to verify members.
        LPWSTR          pwszMember      - LDAP path for object to verify membership.
        BOOL            bVerbose        - IF TRUE, will output verbose information for the scan.
 
    OPTIONAL Parameters
 
       LPOLESTR  pwszUser           - User Name and Password, if the parameters are not passed, 
       LPOLESTER pwszPassword       - binding will use ADsGetObject; if the parameters are
                                    - specified, ADsOpenObject is used, passing user name and password.
*/
 
BOOL RecursiveIsMember(IADsGroup * pADsGroup,LPWSTR pwszMemberGUID,LPWSTR pwszMemberPath, 
                                             BOOL bVerbose, LPOLESTR  pwszUser, LPOLESTR pwszPassword)
{
    HRESULT         hr                = S_OK;     // COM Result Code
    IADsMembers *   pADsMembers       = NULL;     // Ptr to Members of the IADsGroup
    BOOL            fContinue         = TRUE;     // Looping Variable
    IEnumVARIANT *  pEnumVariant      = NULL;     // Ptr to the Enum variant
    IUnknown *      pUnknown          = NULL;     // IUnknown for getting the ENUM initially
    VARIANT         VariantArray[FETCH_NUM];      // Variant array for temp holding returned data
    ULONG           ulElementsFetched = NULL;     // Number of elements retrieved
    BSTR            bsGroupPath       = NULL;
    BOOL            bRet              = FALSE;

    if(!pADsGroup || !pwszMemberGUID || !pwszMemberPath)
    {
        return FALSE;
    }
 
    // Get the path of the object passed in
    hr = pADsGroup->get_ADsPath(&bsGroupPath);
 
    if (!SUCCEEDED(hr))
        return hr;
 
    if (bVerbose)
    {
        WCHAR pwszOutput[2048];
        wsprintf(pwszOutput,L"Checking the Group:\n\n%s\n\n for the member:\n\n%s\n\n",bsGroupPath,pwszMemberPath);
        PrintBanner(pwszOutput);
    }
 
    // Get an interface pointer to the IADsCollection of members
    hr = pADsGroup->Members(&pADsMembers);
 
    if (SUCCEEDED(hr))
    {
        // Query the IADsCollection of members for a new ENUM Interface
        // Be aware that the enum comes back as an IUnknown *
        hr = pADsMembers->get__NewEnum(&pUnknown);
 
        if (SUCCEEDED(hr))
        {
            // QI the IUnknown * for an IEnumVARIANT interface
            hr = pUnknown->QueryInterface(IID_IEnumVARIANT, (void **)&pEnumVariant);
 
            if (SUCCEEDED(hr))
            {
                // While have not hit errors or end of data....
                while (fContinue) 
                {
                   ulElementsFetched = 0;
                    // Get a "batch" number of group members-number of rows specified by FETCH_NUM
                    hr = ADsEnumerateNext(pEnumVariant, FETCH_NUM, VariantArray, &ulElementsFetched);
 
                    if (ulElementsFetched )
                    {
                        // Loop through the current batch-printing the path for each member.
                        for (ULONG i = 0; i < ulElementsFetched; i++ ) 
                        {
                            IDispatch * pDispatch         = NULL; // ptr for holding dispath of element
                            BSTR        bstrCurrentPath   = NULL; // Holds path of object
                            BSTR        bstrGuidCurrent   = NULL; // Holds path of object
                            IDirectoryObject * pIDOCurrent = NULL;// Holds the current object          
 
                            // Get the dispatch ptr for the variant
                            pDispatch = VariantArray[i].pdispVal;
//                            assert(HAS_BIT_STYLE(VariantArray[i].vt,VT_DISPATCH));
 
                            // Get the IADs interface for the "member" of this group
                            hr = pDispatch->QueryInterface(IID_IDirectoryObject,
                                                           (VOID **) &pIDOCurrent ) ;
 
                            if (SUCCEEDED(hr))
                            {
                                // Get the GUID for the current object
                                hr = GetObjectGuid(pIDOCurrent,bstrGuidCurrent);
 
                                if (FAILED(hr))
                                    return hr;
 
                                IADs * pIADsCurrent = NULL;
 
                                // Retrieve the IADs Interface for the current object
                                hr = pIDOCurrent->QueryInterface(IID_IADs,(void**)&pIADsCurrent);
                                if (FAILED(hr))
                                    return hr;
 
                                // Get the ADsPath property for this member
                                hr = pIADsCurrent->get_ADsPath(&bstrCurrentPath);
 
                                if (SUCCEEDED(hr))
                                {
                                    if (bVerbose)
                                        wprintf(L"Comparing:\n\n%s\nWITH:\n%s\n\n",bstrGuidCurrent,pwszMemberGUID);
                                    
                                    // Verify that the member of this group is Equal to passed.
                                    if (_wcsicmp(bstrGuidCurrent,pwszMemberGUID)==0)
                                    {
                                        if (bVerbose)
                                            wprintf(L"!!!!!Object:\n\n%s\n\nIs a member of\n\n%s\n\n",pwszMemberPath,bstrGuidCurrent);   
 
                                        bRet = TRUE;
                                        break;
                                    }
                                    else // Otherwise, bind to this and see if it is a group.
                                    {    // If is it a group then the QI to IADsGroup succeeds
                                        
                                        IADsGroup * pIADsGroupAsMember = NULL;
                                        
                                        if (pwszUser)
                                            hr = ADsOpenObject( bstrCurrentPath,
                                                                pwszUser, 
                                                                pwszPassword, 
                                                                ADS_SECURE_AUTHENTICATION,
                                                                IID_IADsGroup, 
                                                                (void**) &pIADsGroupAsMember);
                                        else
                                            hr = ADsGetObject( bstrCurrentPath, IID_IADsGroup,(void **)&pIADsGroupAsMember);
 
                                        // If bind was completed, then this is a group.
                                        if (SUCCEEDED(hr))
                                        {
                                            // Recursively call this group to verify this group.
                                            BOOL bRetRecurse;
                                            bRetRecurse = RecursiveIsMember(pIADsGroupAsMember,pwszMemberGUID,pwszMemberPath,bVerbose,pwszUser ,pwszPassword );
                                            
                                            if (bRetRecurse)
                                            {
                                                bRet = TRUE;
                                                break;
                                            }
                                            pIADsGroupAsMember->Release();
                                            pIADsGroupAsMember = NULL;
                                        }
                                    }
                                    SysFreeString(bstrCurrentPath);
                                    bstrCurrentPath = NULL;
 
                                    SysFreeString(bstrGuidCurrent);
                                    bstrGuidCurrent = NULL;
                                }
                                // Release
                                pIDOCurrent->Release();
                                pIDOCurrent = NULL;
                                if (pIADsCurrent)
                                {
                                    pIADsCurrent->Release();
                                    pIADsCurrent = NULL;
                                }
                            }
                         }
                        // Clear the variant array.
                        memset(VariantArray, 0, sizeof(VARIANT)*FETCH_NUM);
                    }
                    else
                        fContinue = FALSE;
                }
                pEnumVariant->Release();
                pEnumVariant = NULL;
            }
            pUnknown->Release();
            pUnknown = NULL;
        }
        pADsMembers ->Release();
        pADsMembers  = NULL;
    }
 
    // Free the group path if retrieved.
    if (bsGroupPath)
    {
        SysFreeString(bsGroupPath);
        bsGroupPath = NULL;
    }
    return bRet;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/*  GetObjectGuid()    - Retrieves the GUID, in string form, from the passed Directory Object.
                         Returns S_OK on success.
 
    Parameters
 
        IDirectoryObject *  pDO     -   Directory Object from where GUID is retrieved.
        BSTR             &  bsGuid  -   Returned GUID            
*/
 
HRESULT GetObjectGuid(IDirectoryObject * pDO,BSTR &bsGuid)
{
 
    GUID *pObjectGUID   = NULL;
    PADS_ATTR_INFO      pAttributeEntries;
    LPWSTR              pAttributeName = L"objectGUID";  // Get the GUID for the object.
    DWORD               dwAttributesReturned = 0;
    HRESULT             hr;

    if(!pDO)
    {
        return E_FAIL;
    }

    hr = pDO->GetObjectAttributes(  &pAttributeName, // objectGUID
                                    1, //Only objectGUID
                                    &pAttributeEntries, // Returned attributes
                                    &dwAttributesReturned // Number of attributes returned
                                    );
 
    if (SUCCEEDED(hr) && dwAttributesReturned>0)
    {
        // Be sure the right type was retrieved--objectGUID is ADSTYPE_OCTET_STRING
        if (pAttributeEntries->dwADsType == ADSTYPE_OCTET_STRING)
        {
            // Get COM converted string version of the GUID
            // lpvalue should be LPBYTE. Should be able to cast it to ptr to GUID.
            pObjectGUID = (GUID*)(pAttributeEntries->pADsValues[0].OctetString.lpValue);
            
            // OLE str to fit a GUID
            
            LPOLESTR szGUID = new WCHAR [64];
            szGUID[0]=NULL;
            // Convert GUID to string.
            ::StringFromGUID2(*pObjectGUID, szGUID, 39); 
            bsGuid = SysAllocString(szGUID);
            
            delete [] szGUID;
         }
    }
 
    return hr;
 
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/*  PrintBanner()   -       Prints a banner to the screen
 
    Parameters
 
                LPOLESTR pwszBanner   - String to print
*/
void PrintBanner(LPOLESTR pwszBanner)
{
    _putws(L"");
    _putws(L"////////////////////////////////////////////////////");
    wprintf(L"\t");
    _putws(pwszBanner);
    _putws(L"////////////////////////////////////////////////////\n");
}