/*--------------------------------------------------------------------------- File: ComputerPwdAge.cpp Comments: Implementation of COM object to retrieve password age for computer accounts (used to detect defunct computer accounts.) (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved Proprietary and confidential to Mission Critical Software, Inc. REVISION LOG ENTRY Revision By: Christy Boles Revised on 02/15/99 11:19:31 --------------------------------------------------------------------------- */ // ComputerPwdAge.cpp : Implementation of CComputerPwdAge #include "stdafx.h" #include "WorkObj.h" #include "PwdAge.h" #include "Common.hpp" #include "UString.hpp" #include "Err.hpp" #include "CommaLog.hpp" #include "EaLen.hpp" #include "GetDcName.h" //#import "\bin\McsVarSetMin.tlb" no_namespace //#import "\bin\DBManager.tlb" no_namespace, named_guids #import "VarSet.tlb" no_namespace rename("property", "aproperty") #import "DBMgr.tlb" no_namespace, named_guids #include ///////////////////////////////////////////////////////////////////////////// // CComputerPwdAge STDMETHODIMP CComputerPwdAge::SetDomain( BSTR domain // in - domain name to examine ) { HRESULT hr = S_OK; DWORD rc; WCHAR domctrl[LEN_Computer]; if ( UStrICmp(m_Domain,domain) ) { m_Domain = domain; rc = GetDomainControllerForDomain(domain,domctrl); if ( rc ) { hr = HRESULT_FROM_WIN32(rc); } else { m_DomainCtrl = domctrl; } } return hr; } STDMETHODIMP CComputerPwdAge::GetPwdAge( BSTR domain, // in - domain name to examine BSTR ComputerName, // in - machine name of computer DWORD * pPwdAge // out- password age in seconds ) { HRESULT hr; DWORD rc; WCHAR computerAccountName[LEN_Account]; DWORD pwdage = 0; hr = SetDomain(domain); if ( SUCCEEDED(hr) ) { swprintf(computerAccountName,L"%s",ComputerName); rc = GetSinglePasswordAgeInternal(m_DomainCtrl,computerAccountName,&pwdage); if ( ! rc ) { (*pPwdAge) = pwdage; } else { hr = HRESULT_FROM_WIN32(rc); } } return hr; } STDMETHODIMP CComputerPwdAge::ExportPasswordAge( BSTR domain, // in - domain to export information from BSTR filename // in - UNC name of file to write information to ) { HRESULT hr; hr = SetDomain(domain); if ( SUCCEEDED(hr) ) { hr = ExportPasswordAgeOlderThan(domain,filename,0); } return hr; } STDMETHODIMP CComputerPwdAge::ExportPasswordAgeOlderThan( BSTR domain, // in - domain to export information from BSTR filename, // in - filename to write information to DWORD minAge // in - write only accounts with password age older than minAge ) { DWORD rc = 0; HRESULT hr; hr = SetDomain(domain); if ( SUCCEEDED(hr) ) { rc = ExportPasswordAgeInternal(m_DomainCtrl,filename,minAge,TRUE); if ( rc ) { hr = HRESULT_FROM_WIN32(rc); } } return hr; } STDMETHODIMP CComputerPwdAge::ExportPasswordAgeNewerThan( BSTR domain, // in - domain to export information from BSTR filename, // in - filename to write information to DWORD maxAge // in - write only computer accounts with password age less than maxAge ) { DWORD rc = 0; HRESULT hr; hr = SetDomain(domain); if ( SUCCEEDED(hr) ) { rc = ExportPasswordAgeInternal(m_DomainCtrl,filename,maxAge,FALSE); if ( rc ) { hr = HRESULT_FROM_WIN32(rc); } } return hr; } DWORD // ret- WIN32 error code CComputerPwdAge::GetDomainControllerForDomain( WCHAR const * domain, // in - name of domain WCHAR * domctrl // out- domain controller for domain ) { DWORD rc; _bstr_t result; rc = GetAnyDcName4(domain, result); if ( ! rc ) { wcscpy(domctrl,result); } return rc; } DWORD // ret- WIN32 error code CComputerPwdAge::ExportPasswordAgeInternal( WCHAR const * domctrl, // in - domain controller to query WCHAR const * filename, // in - filename to write results to DWORD minOrMaxAge, // in - optional min or max age to write BOOL bOld // in - TRUE-Age is min age, copies only old accounts ) { DWORD rc = 0; DWORD nRead; DWORD nTotal; DWORD nResume = 0; DWORD prefmax = 1000; USER_INFO_11 * buf; time_t pwdage; // the number of seconds ago that the pwd was last changed time_t pwdtime; // the time when the pwd was last changed time_t now; // the current time CommaDelimitedLog log; IIManageDBPtr pDB; WCHAR computerName[LEN_Account]; rc = pDB.CreateInstance(CLSID_IManageDB); if ( SUCCEEDED(rc) ) { time(&now); do { rc = NetUserEnum(domctrl,11,FILTER_WORKSTATION_TRUST_ACCOUNT,(LPBYTE*)&buf,prefmax,&nRead,&nTotal,&nResume); if ( rc && rc != ERROR_MORE_DATA ) break; for ( UINT i = 0 ; i < nRead ; i++ ) { pwdage = buf[i].usri11_password_age; if ( ( pwdage >= (time_t)minOrMaxAge && bOld ) // inactive machines ||( pwdage <= (time_t)minOrMaxAge && !bOld ) ) // active machines { safecopy(computerName,buf[i].usri11_name); // strip off the $ from the end of the computer account computerName[UStrLen(computerName)-1] = 0; // pDB->raw_SavePasswordAge(m_Domain,SysAllocString(computerName),SysAllocString(buf[i].usri11_comment),pwdage); pDB->raw_SavePasswordAge(m_Domain,SysAllocString(computerName),SysAllocString(buf[i].usri11_comment),(long)pwdage); pwdtime = now - pwdage; } } NetApiBufferFree(buf); } while ( rc == ERROR_MORE_DATA ); } else { rc = GetLastError(); } return rc; } DWORD // ret- WIN32 error code CComputerPwdAge::GetSinglePasswordAgeInternal( WCHAR const * domctrl, // in - domain controller to query WCHAR const * computer, // in - name of computer account DWORD * pwdage // out- password age in seconds ) { DWORD rc = 0; USER_INFO_11 * buf; rc = NetUserGetInfo(domctrl,computer,11,(LPBYTE*)&buf); if (! rc ) { (*pwdage) = buf->usri11_password_age; NetApiBufferFree(buf); } return rc; } // ComputerPwdAge worknode // Retrieves the password age (in seconds) for a computer account in a specified domain // This can be used to identify defunct computer accounts // // VarSet syntax // Input: // ComputerPasswordAge.Domain: // ComputerPasswordAge.Computer: // Output: // ComputerPasswordAge.Seconds : STDMETHODIMP CComputerPwdAge::Process( IUnknown * pWorkItem // in - varset containing settings ) { HRESULT hr = S_OK; IVarSetPtr pVarSet = pWorkItem; _bstr_t domain; _bstr_t computer; DWORD age; domain = pVarSet->get(L"ComputerPasswordAge.Domain"); computer = pVarSet->get(L"ComputerPasswordAge.Computer"); if ( computer.length() && domain.length() ) { hr = GetPwdAge(domain,computer,&age); if ( SUCCEEDED(hr) ) { pVarSet->put(L"ComputerPasswordAge.Seconds",(LONG)age); } } return hr; }