You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
502 lines
15 KiB
502 lines
15 KiB
// UserInfo.cpp: implementation of the CUserInfo class.
|
|
// Adapted from the CUserInfo class in the SBS Add User wizard
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include <sbs6base.h>
|
|
#include "UserInfo.h"
|
|
|
|
#include <atlbase.h>
|
|
#include <comdef.h>
|
|
#include <dsgetdc.h>
|
|
#include <iads.h> // Must come before adshlp.h
|
|
#include <adshlp.h>
|
|
#include <lm.h>
|
|
#include <emolib.h>
|
|
|
|
#include <AUsrUtil.h>
|
|
|
|
#ifndef ASSERT
|
|
#define ASSERT assert
|
|
#endif
|
|
#ifndef WSTRING
|
|
typedef std::wstring WSTRING; // Move to sbs6base.h
|
|
#endif
|
|
|
|
#ifndef CHECK_HR
|
|
#define CHECK_HR( x ) CHECK_HR_RET( x, _hr );
|
|
#define CHECK_HR_RET( x, y ) \
|
|
{ \
|
|
HRESULT _hr = x; \
|
|
if( FAILED(_hr) ) \
|
|
{ \
|
|
ASSERT( !_T("CHECK_HR_RET() failed. calling TRACE() with HRESULT and statement") ); \
|
|
/*::AfxTrace( _T("CHECK_HR_RET() failed, hr == [%d], statement = [%s], returning [%s]"), _hr, #x, #y );*/ \
|
|
return y; \
|
|
} \
|
|
}
|
|
#endif // CHECK_HR
|
|
|
|
// Defines for account flags.
|
|
#define PASSWD_NOCHANGE 0x01
|
|
#define PASSWD_CANCHANGE 0x02
|
|
#define PASSWD_MUSTCHANGE 0x04
|
|
#define ACCOUNT_DISABLED 0x10
|
|
|
|
// ****************************************************************************
|
|
// CUserInfo
|
|
// ****************************************************************************
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// CUserInfo()
|
|
//
|
|
// Constructor.
|
|
// ----------------------------------------------------------------------------
|
|
CUserInfo::CUserInfo()
|
|
{
|
|
m_dwAccountOptions = 0;
|
|
m_bCreateMB = TRUE;
|
|
|
|
// Store the SBS Server Name
|
|
TCHAR szServer[MAX_PATH+1] = {0};
|
|
DWORD dwLen = MAX_PATH;
|
|
GetComputerName(szServer, &dwLen);
|
|
m_csSBSServer = szServer;
|
|
|
|
// Get domain controller name
|
|
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
|
|
HRESULT hr = DsGetDcName (NULL, NULL, NULL, NULL, DS_DIRECTORY_SERVICE_REQUIRED, &DomainControllerInfo);
|
|
ASSERT(hr == S_OK && "DsGetDcName failed");
|
|
|
|
// cache the domain
|
|
if ( DomainControllerInfo )
|
|
{
|
|
m_csDomainName = DomainControllerInfo->DomainName;
|
|
|
|
// free memory
|
|
NetApiBufferFree (DomainControllerInfo);
|
|
}
|
|
|
|
// convert from '.' separated names to Dc=xxx,DC=yyy,... format
|
|
WSTRING tmpDomain = m_csDomainName;
|
|
while ( !tmpDomain.empty() )
|
|
{
|
|
int nPos = tmpDomain.find(_T("."));
|
|
if ( nPos == WSTRING::npos )
|
|
{
|
|
nPos = tmpDomain.length();
|
|
}
|
|
|
|
if ( !m_csFQDomainName.empty() )
|
|
{
|
|
m_csFQDomainName += _T(",");
|
|
}
|
|
|
|
m_csFQDomainName += _T("DC=");
|
|
m_csFQDomainName += tmpDomain.substr( 0, nPos );
|
|
|
|
tmpDomain.erase( 0, nPos + 1 );
|
|
}
|
|
}
|
|
|
|
CUserInfo::~CUserInfo()
|
|
{
|
|
for ( int i = 0 ; i < m_csPasswd.length() ; i++ )
|
|
m_csPasswd[i] = '\0' ;
|
|
if ( 0 < m_csPasswd.length()) m_csPasswd[0] = NULL ;
|
|
// Make sure that the last operation on the string isn't the origonal assignment.
|
|
// The compiler will optimize the last statement out
|
|
// if it is an assignment that doesn't get used.
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// CreateAccount()
|
|
//
|
|
// Makes a new user account in the Active Directory.
|
|
// ----------------------------------------------------------------------------
|
|
HRESULT CUserInfo::CreateAccount()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bRO = TRUE;
|
|
_bstr_t _bstr;
|
|
_bstr_t _bstrClass;
|
|
_bstr_t _bstrProperty;
|
|
|
|
_variant_t _vTmpVal;
|
|
CComPtr<IADsContainer> spADsContainer = NULL;
|
|
|
|
// Bind to the container.
|
|
WSTRING csLdapUserOU;
|
|
if ( _tcsstr( m_csUserOU.c_str(), L"LDAP://") )
|
|
csLdapUserOU = m_csUserOU.c_str();
|
|
else
|
|
{
|
|
if ( m_csUserOU.empty() )
|
|
{
|
|
csLdapUserOU = L"LDAP://CN=Users," + m_csFQDomainName;
|
|
m_csUserOU = csLdapUserOU;
|
|
}
|
|
else
|
|
csLdapUserOU = L"LDAP://" + m_csUserOU;
|
|
}
|
|
hr = ::ADsGetObject( csLdapUserOU.c_str(), IID_IADsContainer, (VOID**) &spADsContainer );
|
|
if ( FAILED(hr) )
|
|
return(hr);
|
|
|
|
// Create the user account.
|
|
CComPtr<IDispatch> spDisp = NULL;
|
|
_bstr = m_csUserCN.c_str();
|
|
_bstrClass = L"user";
|
|
hr = spADsContainer->Create( _bstrClass, _bstr, &spDisp );
|
|
if ( FAILED(hr) )
|
|
{
|
|
ASSERT(FALSE);
|
|
return(hr);
|
|
}
|
|
|
|
// m_pCmt->m_csADName = L"LDAP://";
|
|
// m_pCmt->m_csADName += m_csUserCN;
|
|
// m_pCmt->m_csADName += L",";
|
|
// m_pCmt->m_csADName += (LPCTSTR)csLdapUserOU+7;
|
|
|
|
// Use this new account and set it's properties (e.g. first name, home folder, etc.).
|
|
CComQIPtr<IADsUser, &IID_IADsUser> spADsUserObj(spDisp);
|
|
if ( !spADsUserObj )
|
|
{
|
|
ASSERT(FALSE);
|
|
return(E_FAIL);
|
|
}
|
|
|
|
TCHAR szTmp[MAX_PATH*4] = {0};
|
|
LdapToDCName( csLdapUserOU.c_str(), szTmp, (MAX_PATH*4));
|
|
WSTRING csUserPrincName = m_csUserName;
|
|
csUserPrincName += L"@";
|
|
csUserPrincName += szTmp;
|
|
EmailAddr = csUserPrincName;
|
|
|
|
// Username:
|
|
_vTmpVal.Clear();
|
|
_vTmpVal = ( csUserPrincName.c_str() );
|
|
_bstrProperty = L"userPrincipalName";
|
|
if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
return(E_FAIL);
|
|
}
|
|
|
|
// Pre-Win2000 username:
|
|
_vTmpVal.Clear();
|
|
_vTmpVal = ( _tcslen( m_csUserNamePre2k.c_str() ) ? m_csUserNamePre2k.c_str() : m_csUserName.c_str() );
|
|
_bstrProperty = L"sAMAccountName";
|
|
if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
return(E_FAIL);
|
|
}
|
|
|
|
// Use UserName instead
|
|
_bstr = m_csUserName.c_str();
|
|
if ( FAILED( spADsUserObj->put_FullName( _bstr )))
|
|
{
|
|
ASSERT(FALSE);
|
|
return(E_FAIL);
|
|
}
|
|
|
|
// Display Name
|
|
_vTmpVal.Clear();
|
|
_vTmpVal = m_csUserName.c_str();
|
|
_bstrProperty = L"displayName";
|
|
if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
return(E_FAIL);
|
|
}
|
|
|
|
// Commit this information to the AD.
|
|
CHECK_HR( spADsUserObj->SetInfo() );
|
|
|
|
// Password expired?
|
|
_vTmpVal.Clear();
|
|
V_VT( &_vTmpVal ) = VT_I4;
|
|
V_I4( &_vTmpVal ) = (m_dwAccountOptions & PASSWD_MUSTCHANGE) ? 0 : -1;
|
|
_bstrProperty = L"pwdLastSet";
|
|
if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// Account disabled?
|
|
_vTmpVal.Clear();
|
|
_bstrProperty = L"userAccountControl";
|
|
if ( FAILED(spADsUserObj->Get( _bstrProperty, &_vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
else
|
|
{
|
|
_vTmpVal.lVal &= ~UF_PASSWD_NOTREQD; // Make passwd required.
|
|
if ( m_dwAccountOptions & ACCOUNT_DISABLED ) // Do we want to disable the account?
|
|
_vTmpVal.lVal |= UF_ACCOUNTDISABLE;
|
|
else
|
|
_vTmpVal.lVal &= ~UF_ACCOUNTDISABLE; // ..not disable the account.
|
|
if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
// Set the password.
|
|
hr = SetPasswd();
|
|
if ( FAILED( hr ))
|
|
{
|
|
// m_pCmt->SetErrorResults(ERROR_PASSWORD);
|
|
|
|
_vTmpVal.Clear();
|
|
_bstrProperty = L"userAccountControl";
|
|
if ( FAILED(spADsUserObj->Get( _bstrProperty, &_vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
else
|
|
{
|
|
_vTmpVal.lVal &= ~UF_PASSWD_NOTREQD; // Make passwd required.
|
|
_vTmpVal.lVal |= UF_ACCOUNTDISABLE; // Disable the account.
|
|
|
|
if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) )
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
// Commit this information to the AD.
|
|
if ( FAILED( spADsUserObj->SetInfo() ))
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Commit this information to the AD.
|
|
hr = spADsUserObj->SetInfo();
|
|
if ( FAILED( hr ))
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// CreateMailbox()
|
|
//
|
|
// Makes a new exchange mailbox for the user.
|
|
// ----------------------------------------------------------------------------
|
|
HRESULT CUserInfo::CreateMailbox()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComVariant _vTmpVal;
|
|
CComPtr<IADsContainer> spADsContainer = NULL;
|
|
_bstr_t _bstr;
|
|
_bstr_t _bstrClass;
|
|
|
|
if ( 0 == m_csEXHomeMDB.length() )
|
|
{
|
|
hr = GetMDBPath( m_csEXHomeMDB );
|
|
if FAILED( hr )
|
|
return hr;
|
|
}
|
|
|
|
// Do we even need to run?
|
|
if ( 0 == m_csEXHomeMDB.length() || 0 == m_csEXAlias.length() )
|
|
return(hr);
|
|
|
|
// Bind to the container.
|
|
WSTRING csLdapUserOU = L"LDAP://";
|
|
if ( _tcsstr( m_csUserOU.c_str(), L"LDAP://") )
|
|
csLdapUserOU = m_csUserOU.c_str();
|
|
else
|
|
{
|
|
if ( m_csUserOU.empty() )
|
|
{
|
|
csLdapUserOU = L"LDAP://CN=Users," + m_csFQDomainName;
|
|
m_csUserOU = csLdapUserOU;
|
|
}
|
|
else
|
|
csLdapUserOU = L"LDAP://" + m_csUserOU;
|
|
}
|
|
hr = ::ADsGetObject( csLdapUserOU.c_str(), IID_IADsContainer, (VOID**) &spADsContainer );
|
|
if ( FAILED(hr) || !spADsContainer )
|
|
{
|
|
ASSERT(FALSE);
|
|
return(hr);
|
|
}
|
|
|
|
// Open the user account.
|
|
CComPtr<IDispatch> spDisp = NULL;
|
|
_bstr = m_csUserCN.c_str();
|
|
_bstrClass = L"user";
|
|
hr = spADsContainer->GetObject( _bstrClass, _bstr, &spDisp );
|
|
// if ( !spDisp ) // If the user doesn't exist, create it.
|
|
// hr = spADsContainer->Create( L"user", (LPWSTR)(LPCWSTR)m_csUserCN, &spDisp );
|
|
// return(hr); // Let's just return.. maybe we'll want to change to create later?
|
|
if ( !spDisp ) // Was there a problem getting the account (either existing or new)?
|
|
{
|
|
ASSERT(FALSE);
|
|
return(hr);
|
|
}
|
|
|
|
// Get a handle on the user.
|
|
CComPtr<IADsUser> spADsUserObj;
|
|
hr = spDisp->QueryInterface ( __uuidof(IADsUser), (void**)&spADsUserObj );
|
|
|
|
if ( FAILED(hr) || !spADsUserObj ) // Was there a problem getting the user object?
|
|
{
|
|
ASSERT(FALSE);
|
|
return(hr);
|
|
}
|
|
|
|
// Get the interface needed to deal with the mailbox.
|
|
CComPtr<IMailboxStore> spMailboxStore;
|
|
hr = spADsUserObj->QueryInterface ( __uuidof(IMailboxStore), (void**)&spMailboxStore );
|
|
|
|
if ( FAILED(hr) || !spMailboxStore ) // Was there a problem getting the mailbox store interface?
|
|
{
|
|
ASSERT(FALSE);
|
|
return(hr);
|
|
}
|
|
|
|
// if ( SUCCEEDED( hr ) )
|
|
// { // Need to set the mailNickname first otherwise the alias will always be manufactured from the name.
|
|
// m_csEXAlias = CreateEmailName( m_csEXAlias );
|
|
// m_csEXAlias.TrimLeft();
|
|
// m_csEXAlias.TrimRight();
|
|
// if ( 0 == m_csEXAlias.length( ) )
|
|
// {
|
|
// m_csEXAlias.LoadString( IDS_NOLOC_USEREMAILALIAS );
|
|
// }
|
|
//
|
|
// CComBSTR bszTmp = (LPCTSTR) m_csEXAlias;
|
|
// VARIANT v;
|
|
//
|
|
// VariantInit( &v );
|
|
// V_VT( &v ) = VT_BSTR;
|
|
// V_BSTR( &v ) = bszTmp;
|
|
// hr = spADsUserObj->Put( L"mailNickname", v );
|
|
// }
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{ // Create the mailbox.
|
|
WSTRING csLdapHomeMDB = L"LDAP://";
|
|
csLdapHomeMDB += m_csEXHomeMDB;
|
|
_bstr = csLdapHomeMDB.c_str();
|
|
hr = spMailboxStore->CreateMailbox( _bstr );
|
|
|
|
if ( hr == S_OK ) // If it's a new mailbox, set the info.
|
|
{
|
|
hr = spADsUserObj->SetInfo();
|
|
}
|
|
|
|
if ( HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS ) // If it already exists, just move on.
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
if ( FAILED(hr) ) // Was there a problem?
|
|
{
|
|
ASSERT(FALSE);
|
|
return(hr);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// SetPasswd()
|
|
// ----------------------------------------------------------------------------
|
|
HRESULT CUserInfo::SetPasswd()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WSTRING csUser = _T("LDAP://");
|
|
|
|
if ( _tcsstr( m_csUserOU.c_str(), _T("LDAP://")) )
|
|
{
|
|
csUser += m_csUserCN;
|
|
csUser += _T(",");
|
|
csUser += m_csUserOU.c_str() + 7;
|
|
}
|
|
else
|
|
{
|
|
csUser += m_csUserCN;
|
|
csUser += _T(",");
|
|
csUser += m_csUserOU.c_str();
|
|
}
|
|
|
|
// Now csUser is something like "LDAP://CN=JohnDoe,DC=Blah"
|
|
CComPtr<IADsUser> spDS = NULL;
|
|
hr = ::ADsGetObject( csUser.c_str(), IID_IADsUser, (void**)&spDS );
|
|
CHECK_HR(hr);
|
|
|
|
// Set the password.
|
|
if ( m_csPasswd.length() ) // Only if there IS a passwd!
|
|
{
|
|
CComBSTR bszPasswd = m_csPasswd.c_str();
|
|
|
|
hr = spDS->SetPassword(bszPasswd);
|
|
CHECK_HR(hr);
|
|
}
|
|
|
|
// Allow change?
|
|
if ( m_dwAccountOptions & PASSWD_NOCHANGE )
|
|
{
|
|
CComVariant vaTmpVal;
|
|
CComBSTR bstrProp = _T("UserFlags");
|
|
|
|
vaTmpVal.Clear();
|
|
hr = spDS->Get( bstrProp, &vaTmpVal );
|
|
CHECK_HR(hr);
|
|
|
|
vaTmpVal.lVal |= UF_PASSWD_CANT_CHANGE;
|
|
|
|
hr = spDS->Put( bstrProp, vaTmpVal );
|
|
CHECK_HR(hr);
|
|
}
|
|
|
|
// SetInfo only if we actually changed anything.
|
|
if ( ( m_csPasswd.length()) || // Did we mess with the passwd?
|
|
( m_dwAccountOptions & PASSWD_NOCHANGE ) ) // Did we make it unable to change?
|
|
{
|
|
hr = spDS->SetInfo(); // If either, then set the new info.
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// SetUserName
|
|
// Set the user name and dependant member variables (m_csUserCN, m_csEXAlias)
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT CUserInfo::SetUserName( LPCTSTR szUserName )
|
|
{
|
|
m_csUserName = szUserName;
|
|
m_csUserCN = L"CN=" + m_csUserName;
|
|
m_csEXAlias = szUserName;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// SetPassword
|
|
// Set the Password
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT CUserInfo::SetPassword( LPCTSTR szPassword )
|
|
{
|
|
m_csPasswd = szPassword;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|