/*
**  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");
}