Platform SDK: Active Directory, ADSI, and Directory Services |
The following code fragments enumerate the properties of the specified user in the current domain, by searching for the user and then using IADsPropertyList to enumerate its properties (note how time/date values as large integers are handled and how octet strings for objectSID and objectGUID are handled.).
//For the pow function to calculate powers of 2 #include <math.h> #include <wchar.h> #include <objbase.h> #include <activeds.h> //Make sure you define UNICODE //Need to define version 5 for Windows 2000 #define _WIN32_WINNT 0x0500 //For SID conversion API. #include <sddl.h> HRESULT GetUserProperties(IADs * pObj); HRESULT EnumeratePropertyValue( IADsPropertyEntry *pEntry ); HRESULT FindUserByName(IDirectorySearch *pSearchBase, //Container to search LPOLESTR szFindUser, //Name of user to find. IADs **ppUser); //Return a pointer to the user void wmain( int argc, wchar_t *argv[ ]) { //Handle the command line arguments. LPOLESTR pszBuffer = new OLECHAR[MAX_PATH*2]; if (argv[1] == NULL) { wprintf(L"This program finds a user in the current Window 2000 domain\n"); wprintf(L"and displays its properties.\n"); wprintf(L"Enter Common Name of the user to find:"); _getws(pszBuffer); } else wcscpy(pszBuffer, argv[1]); //if empty string, exit. if (0==wcscmp(L"", pszBuffer)) return; wprintf(L"\nFinding user: %s...\n",pszBuffer); //Intialize COM CoInitialize(NULL); HRESULT hr = S_OK; //Get rootDSE and the domain container's DN. IADs *pObject = NULL; IDirectorySearch *pDS = NULL; LPOLESTR szPath = new OLECHAR[MAX_PATH]; VARIANT var; hr = ADsOpenObject(L"LDAP://rootDSE", NULL, NULL, ADS_SECURE_AUTHENTICATION, //Use Secure Authentication IID_IADs, (void**)&pObject); if (FAILED(hr)) { wprintf(L"Not Found. Could not bind to the domain.\n"); if (pObject) pObject->Release(); return; } hr = pObject->Get(L"defaultNamingContext",&var); if (SUCCEEDED(hr)) { wcscpy(szPath,L"LDAP://"); wcscat(szPath,var.bstrVal); VariantClear(&var); if (pObject) { pObject->Release(); pObject = NULL; } //Bind to the root of the current domain. hr = ADsOpenObject(szPath, NULL, NULL, ADS_SECURE_AUTHENTICATION, //Use Secure Authentication IID_IDirectorySearch, (void**)&pDS); if (SUCCEEDED(hr)) { hr = FindUserByName(pDS, //Container to search pszBuffer, //Name of user to find. &pObject); //Return a pointer to the user if (SUCCEEDED(hr)) { wprintf (L"----------------------------------------------\n"); wprintf (L"--------Call GetUserProperties-----------\n"); hr = GetUserProperties(pObject); wprintf (L"GetUserProperties HR: %x\n", hr); } else { wprintf(L"User \"%s\" not Found.\n",pszBuffer); wprintf (L"FindUserByName failed with the following HR: %x\n", hr); } if (pObject) pObject->Release(); } if (pDS) pDS->Release(); } //uninitialize COM CoUninitialize(); return; } HRESULT GetUserProperties(IADs * pObj) { HRESULT hr = E_FAIL; LPOLESTR szDSPath = new OLECHAR [MAX_PATH]; IADsPropertyList *pObjProps = NULL; IADsPropertyEntry *pEntry = NULL; VARIANT var; BSTR szString; long lCount = 0L; long lCountTotal = 0L; long lPType = 0L; if (!pObj) return E_INVALIDARG; //Call GetInfo to load all properties for the object into the cache. //Must do this because IADsPropertyList methods read from the cache. hr = pObj->GetInfo(); if (SUCCEEDED(hr)) { //QI for an IADsPropertyList pointer. hr = pObj->QueryInterface(IID_IADsPropertyList, (void**)&pObjProps); if (SUCCEEDED(hr)) { //Enumerate the properties of the object hr = pObjProps->get_PropertyCount(&lCountTotal); wprintf(L"Property Count: %d\n",lCountTotal); hr = pObjProps->Next(&var); if (SUCCEEDED(hr)) { lCount = 1L; while (hr==S_OK) { if (var.vt==VT_DISPATCH) { hr = V_DISPATCH(&var)->QueryInterface(IID_IADsPropertyEntry, (void**)&pEntry); if (SUCCEEDED(hr)) { hr = pEntry->get_Name(&szString); wprintf(L"%s: ",szString); SysFreeString(szString); hr = pEntry->get_ADsType(&lPType); if (lPType != ADSTYPE_INVALID) { hr = EnumeratePropertyValue(pEntry); if(FAILED(hr)) printf("EnumeratePropertyValue failed. hr: %x\n",hr); } else { wprintf(L"Invalid type\n"); } } else { printf("IADsPropertyEntry QueryInterface call failed. hr: %x\n",hr); } //Clean up if (pEntry) pEntry->Release(); } else { printf("Unexpected returned type for VARIANT: %d",var.vt); } VariantClear(&var); hr = pObjProps->Next(&var); if (SUCCEEDED(hr)) { lCount++; } } } } wprintf(L"Total properties retrieved: %d\n",lCount); //Clean up if (pObjProps) pObjProps->Release(); } //Return success if all properties were retrieved. if (lCountTotal==lCount) hr=S_OK; return hr; } HRESULT EnumeratePropertyValue( IADsPropertyEntry *pEntry ) { HRESULT hr = E_FAIL; IADsPropertyValue *pValue = NULL; IADsLargeInteger *pLargeInt = NULL; long lType, lValue; BSTR bstr,szString; VARIANT var,varOS,varDate; VARIANT *pVar; CHAR *pszBOOL = NULL; FILETIME filetime; SYSTEMTIME systemtime; IDispatch *pDisp = NULL; DATE date; //For Octet Strings void HUGEP *pArray; ULONG dwSLBound; ULONG dwSUBound; hr = pEntry->get_Values(&var); if (SUCCEEDED(hr)) { //Should be safe array containing variants if (var.vt==(VT_VARIANT|VT_ARRAY)) { hr = SafeArrayAccessData((SAFEARRAY*)(var.pparray), (void HUGEP* FAR*)&pVar); long lLBound, lUBound; //One dimensional array. Get the bounds for the array. hr = SafeArrayGetLBound((SAFEARRAY*)(var.pparray), 1, &lLBound); hr = SafeArrayGetUBound((SAFEARRAY*)(var.pparray), 1, &lUBound); //Get the count of elements long cElements = lUBound-lLBound + 1; //Get the elements of the array if (SUCCEEDED(hr)) { for (int i = 0; i < cElements; i++ ) { switch (pVar[i].vt) { case VT_BSTR: wprintf(L"%s ",pVar[i].bstrVal); break; case VT_DISPATCH: hr = V_DISPATCH(&pVar[i])->QueryInterface(IID_IADsPropertyValue, (void**)&pValue); if (SUCCEEDED(hr)) { hr = pValue->get_ADsType(&lType); switch (lType) { case ADSTYPE_DN_STRING: hr = pValue->get_DNString(&bstr); wprintf(L"%s ",bstr); SysFreeString(bstr); break; case ADSTYPE_CASE_IGNORE_STRING: hr = pValue->get_CaseIgnoreString(&bstr); wprintf(L"%s ",bstr); SysFreeString(bstr); break; case ADSTYPE_BOOLEAN: hr = pValue->get_Boolean(&lValue); pszBOOL = lValue ? "TRUE" : "FALSE"; wprintf(L"%s ",pszBOOL); break; case ADSTYPE_INTEGER: hr = pValue->get_Integer(&lValue); wprintf(L"%d ",lValue); break; case ADSTYPE_OCTET_STRING: //Get the name of the property to handle //the properties we're interested in. pEntry->get_Name(&szString); hr = pValue->get_OctetString(&varOS); //Get a pointer to the bytes in the octet string. if (SUCCEEDED(hr)) { hr = SafeArrayGetLBound( V_ARRAY(&varOS), 1, (long FAR *) &dwSLBound ); hr = SafeArrayGetUBound( V_ARRAY(&varOS), 1, (long FAR *) &dwSUBound ); if (SUCCEEDED(hr)) { hr = SafeArrayAccessData( V_ARRAY(&varOS), &pArray ); } if (0==wcscmp(L"objectGUID", szString)) { LPOLESTR szDSGUID = new WCHAR [39]; //Cast to LPGUID LPGUID pObjectGUID = (LPGUID)pArray; //Convert GUID to string. ::StringFromGUID2(*pObjectGUID, szDSGUID, 39); //Print the GUID wprintf(L"%s ",szDSGUID); } else if (0==wcscmp(L"objectSid", szString)) { PSID pObjectSID = (PSID)pArray; //Convert SID to string. LPOLESTR szSID = NULL; ConvertSidToStringSid(pObjectSID, &szSID); wprintf(L"%s ",szSID); LocalFree(szSID); } else { wprintf(L"Value of type Octet String. No Conversion."); } SafeArrayUnaccessData( V_ARRAY(&varOS) ); VariantClear(&varOS); } SysFreeString(szString); break; case ADSTYPE_UTC_TIME: //wprintf(L"Value of type UTC_TIME\n"); hr = pValue->get_UTCTime(&date); if (SUCCEEDED(hr)) { //Pack in variant.vt varDate.vt = VT_DATE; varDate.date = date; VariantChangeType(&varDate,&varDate,VARIANT_NOVALUEPROP,VT_BSTR); wprintf(L"%s ",varDate.bstrVal); VariantClear(&varDate); } break; case ADSTYPE_LARGE_INTEGER: //wprintf(L"Value of type Large Integer\n"); //Get the name of the property to handle //the properties we're interested in. pEntry->get_Name(&szString); hr = pValue->get_LargeInteger(&pDisp); if (SUCCEEDED(hr)) { hr = pDisp->QueryInterface(IID_IADsLargeInteger, (void**)&pLargeInt); if (SUCCEEDED(hr)) { hr = pLargeInt->get_HighPart((long*)&filetime.dwHighDateTime); hr = pLargeInt->get_LowPart((long*)&filetime.dwLowDateTime); if((filetime.dwHighDateTime==0) && (filetime.dwLowDateTime==0)) { wprintf(L"No Value "); } else { //Check for properties of type LargeInteger that represent time //if TRUE, then convert to variant time. if ((0==wcscmp(L"accountExpires", szString))| (0==wcscmp(L"badPasswordTime", szString))|| (0==wcscmp(L"lastLogon", szString))|| (0==wcscmp(L"lastLogoff", szString))|| (0==wcscmp(L"lockoutTime", szString))|| (0==wcscmp(L"pwdLastSet", szString)) ) { //Handle special case for Never Expires where low part is -1 if (filetime.dwLowDateTime==-1) { wprintf(L"Never Expires "); } else { if (FileTimeToLocalFileTime(&filetime, &filetime) != 0) { if (FileTimeToSystemTime(&filetime, &systemtime) != 0) { if (SystemTimeToVariantTime(&systemtime, &date) != 0) { //Pack in variant.vt varDate.vt = VT_DATE; varDate.date = date; VariantChangeType(&varDate,&varDate,VARIANT_NOVALUEPROP,VT_BSTR); wprintf(L"%s ",varDate.bstrVal); VariantClear(&varDate); } else { wprintf(L"FileTimeToVariantTime failed "); } } else { wprintf(L"FileTimeToSystemTime failed "); } } else { wprintf(L"FileTimeToLocalFileTime failed "); } } } //Print the LargeInteger. else { wprintf(L"Large Integer: high: %d low: %d ",filetime.dwHighDateTime, filetime.dwLowDateTime); } } } if (pLargeInt) pLargeInt->Release(); } else { wprintf(L"Could not get Large Integer"); } if (pDisp) pDisp->Release(); break; case ADSTYPE_NT_SECURITY_DESCRIPTOR: wprintf(L"Value of type NT Security Descriptor "); break; case ADSTYPE_PROV_SPECIFIC: wprintf(L"Value of type Provider Specific "); break; default: wprintf(L"Unhandled ADSTYPE for property value: %d ",lType); break; } } else { wprintf(L"QueryInterface failed for IADsPropertyValue. HR: %x\n", hr); } if (pValue) pValue->Release(); break; default: wprintf(L"Unhandled Variant type for property value array: %d\n",pVar[i].vt); break; } } wprintf(L"\n"); } //Decrement the access count for the array. SafeArrayUnaccessData((SAFEARRAY*)(var.pparray)); } } return hr; } HRESULT FindUserByName(IDirectorySearch *pSearchBase, //Container to search LPOLESTR szFindUser, //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"(&(objectCategory=person)(objectClass=user)(cn="); 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 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 requested attribute is type CaseIgnoreString. wcscpy(szADsPath, col.pADsValues->CaseIgnoreString); hr = ADsOpenObject(szADsPath, NULL, NULL, ADS_SECURE_AUTHENTICATION, //Use Secure Authentication IID_IADs, (void**)ppUser); if (SUCCEEDED(hr)) { wprintf(L"Found User.\n",szFindUser); wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); hrObj = S_OK; } pSearchBase->FreeColumn( &col ); } else hr = E_FAIL; } } // Close the search handle to clean up pSearchBase->CloseSearchHandle(hSearch); } if (FAILED(hrObj)) hr = hrObj; return hr; }
Dim sUserName As String Dim sSearchFilter As String Dim lScope As Integer Dim iIndex As Integer iIndex = 0 Dim v, j, i Dim ds As IADs Dim con As New Connection, rs As New Recordset Dim Com As New Command Dim oIADs As IADs Dim sLdap As String Dim sUserADsPath As String sUserName = InputBox("This script Enumerates the properties of a user on a domain." & vbCrLf & vbCrLf & "Specify the name of the user:") If sUserName = "" Then Exit Sub End If ' Bind to the rootDSE Set ds = GetObject("LDAP://RootDSE") sLdap = "LDAP://" & ds.Get("defaultNamingContext") Set ds = Nothing Set oIADs = GetObject(sLdap) sSearchFilter = "CN='" & sUserName & "'" 'Open a Connection object con.Provider = "ADsDSOObject" '----------------------------------------------------------------- ' To be authenticated using alternate credentials ' use connection properties of User ID and Password '----------------------------------------------------------------- ' con.Properties("User ID") = "Administrator" ' con.Properties("Password") = "" ' Open the connection con.Open "Active Directory Provider" ' Create a command object on this connection Set Com.ActiveConnection = con ' set the query string using SQL Dialect Com.CommandText = "select name,AdsPath from '" & oIADs.ADsPath & "' where " & sSearchFilter & " ORDER BY NAME" ' Tell the user what the search filter is 'MsgBox "Search Filter = " & Com.CommandText '--------------------------------------------------- ' Or you can use LDAP Dialect, for example, '--------------------------------------------------- ' Ex Com.CommandText = "<LDAP://ntdsdc1/dc=NTDEV,DC=microsof,DC=com>;(objectClass=*);name" ' For LDAP Dialect, the valid search scope are base, oneLevel and subtree ' Com.CommandText = "<" & adDomainPath & ">;(objectClass=*);name;subtree" ' For LDAP Dialect (<LDAP:...>), there is no way to specify sort order in the string, ' However, you can use this SORT ON property to specify sort order. ' for SQL Dialect you can use ORDER BY in the SQL Statement ' Ex. Com.Properties("Sort On") = "Name" 'Set the preferences for Search Com.Properties("Page Size") = 1000 Com.Properties("Timeout") = 30 'seconds Com.Properties("searchscope") = ADS_SCOPE_SUBTREE Com.Properties("Chase referrals") = ADS_CHASE_REFERRALS_EXTERNAL Com.Properties("Cache Results") = False ' do not cache the result, it results in less memory requirements Com.Properties("Size Limit") = 1 ' Limit to 1 Result 'Execute the query Set rs = Com.Execute ' Navigate the record set If Not rs.EOF Then rs.MoveFirst End If On Error Resume Next If Not rs.EOF Then ' Display the LDAP path for the row MsgBox "Found the user " & sUserName & " at " & rs.Fields("AdsPath") sUserADsPath = rs.Fields("AdsPath") rs.MoveNext Else MsgBox "Did not find the username " & sUserName & " in the directory" Exit Sub End If Set ds = Nothing Set con = Nothing Set rs = Nothing Set Com = Nothing Set oIADs = Nothing ' Now enumerate the properties Dim propList As IADsPropertyList Dim propEnty As IADsPropertyEntry Dim propVal As IADsPropertyValue Dim count As Long Dim sOutput As String Dim currentcount As Long Const NumToDisplayAtAtime As Integer = 10 ' Bind to the user Set propList = GetObject(sUserADsPath) ' Bring the properties into the cache propList.GetInfo count = propList.PropertyCount sOutput = "No of Property Found: " & Str(count) & vbCrLf & vbCrLf For i = 0 To count - 1 currentcount = currentcount + 1 'Each item in property list has a property entry Set propEntry = propList.Item(i) ' Append to outputstring sOutput = sOutput & "PROPERTYENTRY NAME:" & propEntry.Name & vbCrLf & " ------" & vbCrLf 'Each value in property entry has property values For Each v In propEntry.Values Set propVal = v ' Append to outputstring sOutput = sOutput & propVal.CaseIgnoreString vbCrLf Next If currentcount = NumToDisplayAtAtime Then MsgBox sOutput sOutput = "" currentcount = 0 End If Next Set propList = Nothing Set propEnty = Nothing Set propVal = Nothing