/*
** Machineacc.cpp creates a machine account on an Windows 2000 domain
** using ADSI and the LDAP provider.
**
** Libraries: activeds.lib, adsiid.lib
**
** Copyright (c) 1998 Microsoft Corporation
** All Rights Reserved
**
*/
#include "stdafx.h"
#define UF_WORKSTATION_TRUST_ACCOUNT 0x1000
#define UF_ACCOUNTDISABLE 0x2
#define ADS_GUID_COMPUTRS_CONTAINER L"aa312825768811d1aded00c04fd8d5cd"
#define ADS_ACETYPE_ACCESS_ALLOWED 0
#define ADS_ACEFLAG_INHERIT_ACE 2
HRESULT workWithSD(VARIANT pAds, WCHAR *Trustee);
IADsAccessControlEntry *createAce(
long mask,
long type,
long flag,
BSTR trustee);
void ParseCommandLine( int iCount, char *args[], BSTR *newmachine,
BSTR *newmachine2, BSTR *newmachinepassword, WCHAR *trustee);
void PrintUsage( char * );
int main( int argc, char *argv[])
{
IADs *pNS = NULL,
*pRoot=NULL,
*pIADs = NULL,
*pAuth=NULL;
IDirectoryObject *pCompContObject=NULL; //pICompDirObj
IADsGroup *pGrp = NULL;
IADsContainer *pICompContainer= NULL;
IADsComputer *pIComputer = NULL;
WCHAR adsPath[255];
IADsOpenDSObject *pDSObj=NULL;
HRESULT hr;
DWORD lFlag;
VARIANT var;
VariantInit(&var);
//----PARAMETERS ----
lFlag = UF_WORKSTATION_TRUST_ACCOUNT | UF_ACCOUNTDISABLE;
//----BUILD WELL-KNOWN GUID ADSPATH FOR COMPUTER CONTAINER----
hr = CoInitialize(NULL);
//
//Allocate bstrs for the object class, the new machine name, and the
//machine accounts user name.
//
BSTR computerclass = SysAllocString(L"computer");
BSTR newmachinename, newmachinename2, newmachinepassword;
WCHAR Trustee[255];
ParseCommandLine( argc, argv, &newmachinename, &newmachinename2, &newmachinepassword, Trustee);
//
// You need an IADs interface for the LDAP provider.
//
hr = ADsGetObject(TEXT("LDAP://rootDSE"),
IID_IADs,
(void**)&pRoot);
if (FAILED(hr)) {
wprintf(L"Bind to rootDSE failed: 0x%x\n", hr);
return hr;
}
hr = pRoot->Get(L"defaultNamingContext",&var);
wcscpy(adsPath,L"LDAP://<WKGUID=");
wcscat(adsPath,ADS_GUID_COMPUTRS_CONTAINER);
wcscat(adsPath,L",");
wcscat(adsPath,var.bstrVal);
wcscat(adsPath,L">");
hr = ADsGetObject(adsPath, IID_IADs, (void**) &pIADs);
// Bind again to get the correct ADsPath.
hr = pIADs->Get(L"distinguishedName",&var);
wcscpy(adsPath,L"LDAP://");
wcscat(adsPath,var.bstrVal);
pIADs->Release();
//
// You need an IDispatch pointer for the new computer object,
// declare one.
//
IDispatch *pIDispatch;
//
// Retrieve the IAdsContainer interfaces from the Computers container.
// Use the Create method to obtain an IDispatch pointer to the new computer account
// Then QI for the IAdsUser interface. This interfaces allow you to
// set the samAccountName for the computer account (new machine with a "$" appended)
//
hr = ADsGetObject(adsPath, IID_IDirectoryObject, (void**) &pCompContObject);
hr = pCompContObject->QueryInterface(IID_IADsContainer,(void**) &pICompContainer);
hr = pICompContainer->Create(computerclass,newmachinename, &pIDispatch);
//
// Set the property BSTR with the samAccountName property and set the
// password for the machine account. The initial password is the same
// as the computer name. A new password is to be negotiated between the
// machine the domain on a periodic basis.
//
BSTR property = SysAllocString(L"samAccountName");
IADsUser*pCompUser = NULL;
//
// Set up the variant structure for the computer account name.
//
VariantInit(&var);
var.vt = VT_BSTR;
var.bstrVal = newmachinename2;
//
// Need an IAdsUser interface, QI the IDispatch pointer of the new computer object
// for it.
//
hr = pIDispatch->QueryInterface(IID_IADsUser, (void**) &pCompUser);
//
// Set the samAccountName and other properties of the
// the machine account.
//
hr = pCompUser->Put(property,var);
//
// Set up the Variant structure with the userAccountControl flags.
//
VariantInit(&var);
var.vt=VT_I4;
var.lVal = (LONG) lFlag;
SysFreeString( property);
property = SysAllocString(L"userAccountControl");
hr = pCompUser->Put(property,var);
//
// Be sure to flush the changes in the local ADSI cache to the DC
// by using the SetInfo call on the
// user object.
//
hr = pCompUser->SetInfo();
//
// Set the initial password on the machine account.
//
hr = pCompUser->SetPassword(newmachinepassword);
//
// Free the system allocated BSTR values.
//
SysFreeString(property);
SysFreeString(newmachinename);
SysFreeString(newmachinepassword);
SysFreeString(computerclass);
//
//
// Obtain the machine account SID and add some entries to its ACL.
// The ntSecurityDesciptor property returns as an IDispatch pointer in the
// variant var
//
property=SysAllocString(L"ntSecurityDescriptor");
VariantClear(&var);
hr = pCompUser->Get(property,&var);
//
// Set the sid.
// AddRef the IDispatch pointer because we are handing it off
// to another routine.
//
V_DISPATCH(&var)->AddRef();
//
// WorkWithSD adds entries to the ACL. ( See the below)
//
hr = workWithSD(var, Trustee);
//
// Put the modified security descriptor back into the ntSecurityDescriptor property.
// Note that this is done on the local system's ADSI cache; force the changes to
// the AD by calling pCompUser->SetInfo.
//
hr=pCompUser->Put(property,var);
hr = pCompUser->SetInfo();
V_DISPATCH(&var)->Release();
VariantClear(&var);
//
//Enable account by setting the AccountDisbled property to 0
//
var.vt=VT_BOOL;
var.boolVal = 0;
hr = pCompUser->put_AccountDisabled (0);
hr = pCompUser->SetInfo();
//
// Clean up.
//
if( pCompUser) pCompUser->Release();
if( pIDispatch) pIDispatch->Release();
if( pCompContObject ) pCompContObject->Release();
if( pICompContainer ) pICompContainer->Release();
return S_OK;
}
//
// WorkWithSD takes a Variant pointer to an IDispatch interface
// for a security descriptor.
//
// Retrieves the discretionary ACL and adds the necessary entries for a computer account.
//
HRESULT workWithSD(VARIANT pAds, WCHAR *Trustee)
{
HRESULT hr;
//
// We have an IDispatch pointer, we need an IADsSecurityDescriptor pointer.
// Request one from the IDispatch pointer using the QI method.
//
IADsSecurityDescriptor *pSD;
hr = V_DISPATCH(&pAds)->QueryInterface(IID_IADsSecurityDescriptor,
(void**)&pSD);
if(!pSD) {
V_DISPATCH(&pAds)->Release();
return (0);
}
//
// The Discretionary ACL is returned as an IDispatch pointer,
// Declare and IDispatch pointer and then QI the pointer for an IADsAccessControlList
// pointer.
//
IDispatch *pDisp;
IADsAccessControlList *dacl;
hr = pSD->get_DiscretionaryAcl(&pDisp);
hr = pDisp->QueryInterface(IID_IADsAccessControlList,
(void**)&dacl);
IADsAccessControlEntry *pAce;
//
// Declare an ace pointer, then use the createAce function to create
// one for the appropriate trustee.
//
pAce = createAce(-1, // full permissions
ADS_ACETYPE_ACCESS_ALLOWED,
ADS_ACEFLAG_INHERIT_ACE,
Trustee);
//
// The AddAce method on the ACL object expects a IDispatch pointer, not
// a IAdsAccessControlEntry pointer, QI for the IDispatch pointer from the
// Ace, then use this IDispatch pointer as the argument to the AddAce method
// on the ACL.
//
IDispatch *pAceDisp;
hr = pAce->QueryInterface(IID_IDispatch,(void**)&pAceDisp);
if(FAILED(hr)) {
pAce->Release();
return hr;
}
//
// Add the IDispatch pointer for the ACE to the ACL.
// Because you are done with it release it.
//
hr = dacl->AddAce(pAceDisp);
pAceDisp->Release();
//
// Remember the pDisp pointer that contains an IDispatch pointer for
// the ACL.
//
// Put the ACL back into the SD using the IDispatch pointer for the
// ACL, then clean up and exit.
//
hr = pSD->put_DiscretionaryAcl(pDisp);
if(dacl) dacl->Release();
if(pSD) pSD->Release();
if(pAce) pAce->Release();
return S_OK;
}
////////////////////////////////////
// Function to create an allowed ACE
////////////////////////////////////
IADsAccessControlEntry *createAce(
long mask,
long type,
long flag,
BSTR trustee)
{
HRESULT hr;
IADsAccessControlEntry *pAce;
hr = CoCreateInstance(CLSID_AccessControlEntry,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsAccessControlEntry,
(void**)&pAce);
if(FAILED(hr)) {
if(pAce) pAce->Release();
return NULL;
}
hr = pAce->put_AccessMask(mask);
hr = pAce->put_AceType(type);
hr = pAce->put_AceFlags(flag);
hr = pAce->put_Trustee(trustee);
return pAce;
}
//
// Parse the command line for the sample. Command line should look like this:
//
// app new_machine_account_name Trustee
//
// Trustee should have the form: Domain/[user|group]
//
// Function returns the new machine name, the SamAccountname and the trustee
// to the caller or it forces termination of the application.
//
void ParseCommandLine( int iCount, char *args[], BSTR *newmachine, BSTR *newmachine2, BSTR *newmachinepassword, WCHAR *trustee)
{
WCHAR *arg1, *arg2, *tmp;
if( iCount < 2 )
{
PrintUsage( args[0] );
exit(1);
}
//
// We have the correct number of arguments,
// convert the args to WCHAR from char and add the CN=
// to create a valid common name property for the machine account.
//
arg1 = new WCHAR[strlen(args[1])+1]; // Allocate space
tmp = new WCHAR[strlen(args[1])+4]; // Allocate the temp buffer with space for CN=
tmp[0] = (WCHAR)NULL; // Terminate the temp buffer.
wcscat( tmp, L"CN="); // Add the CN= to the temp buffer.
mbstowcs( arg1, args[1], strlen(args[1])); // Convert the machine name. to unicode
arg1[strlen(args[1])] = (WCHAR)NULL; // Terminate the machine name.
wcscat( tmp, arg1); // Add it to the temp buffer.
mbstowcs( trustee, args[2], strlen(args[2])); // Convert the trustee. argument to UNICODE
trustee[strlen(args[2])] = (WCHAR)NULL; // Terminate it
//
// Convert the machine name to lower case without the CN=
//
WCHAR *str;
for( str = arg1; str < arg1 + wcslen(arg1); str++) *str = towlower( *str );
//
// Create Sam Account Name.
//
arg2 = new WCHAR[strlen(args[1])+2]; // Allocate the sam account. name
mbstowcs( arg2, args[1], strlen(args[1])); // Convert the sam account. to UNICODE
arg2[strlen(args[1])] = (WCHAR)NULL; // Terminate it.
wcscat( arg2, L"$"); // Add the $ sign to the end to make it valid.
//
// Allocate BSTRs.
//
*newmachine = SysAllocString( tmp );
*newmachine2 = SysAllocString( arg2 );
*newmachinepassword = SysAllocString( arg1);
//
// Clean up.
//
delete[] tmp;
delete[] arg1;
delete[] arg2;
return;
}
//
// PrintUsage
// Prints command line help.
//
void PrintUsage( char *ProgName)
{
printf("ERROR: Wrong number of arguments.\n\n");
printf("Command line : %s New_Machine Trustee\n", ProgName );
printf("Where:\nNew_Machine is the new machine account name\n");
printf("Trustee is the Trustee of the account in the form of Domain\\[user|group]\n");
printf("Halting Execution.\n");
}