MACHACCT.C

/*++ 

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

machacct.c

Abstract:

The following sample illustrates how to create a machine account on the
specified domain.

Machine account types are defined by the following flags:

UF_SERVER_TRUST_ACCOUNT (Backup domain controller)
UF_WORKSTATION_TRUST_ACCOUNT (Workstation and server)
UF_INTERDOMAIN_TRUST_ACCOUNT (Interdomain trust account)

This sample attempts to create a workstation machine account, of type
UF_WORKSTATION_TRUST_ACCOUNT. This account type used for workstations
and non-DC servers which are domain members.

If the computer account creation fails with GetLastError ==
ERROR_ACCESS_DENIED, the sample attempts to enable the
SeMachineAccountPrivilege for the caller. If the privilege is enabled
successfully, the computer account add operation is re-tried.

Deleting machine accounts can be accomplished using the NetUserDel()
Windows NT Lan Manager API call.

Account update operations against a domain must be performed against
the primary domain controller for the specified domain. This sample
uses the NetGetDCName Windows NT Lan Manager API call to determine
the computer name of the primary domain controller.

Commandline parameter argv[1] indicates the name of the account to
create, which is typically the name of the machine.

Commandline parameter argv[2] is optional and indicates the target domain.
If this commandline argument is omitted, the machine account is created
on the local domain. It is recommended that you always supply a domain
name in this sample, as this insures that the update occurs at the primary
domain controller.

The following commandline creates a machine account named WINBASE in
the domain named NTWKSTA

machacct.exe WINBASE NTWKSTA

Author:

Scott Field (sfield) 16-Apr-96
Minor cleanup

Scott Field (sfield) 19-Sep-95

--*/

#include <windows.h>
#include <stdio.h>
#include <lm.h>

BOOL
AddMachineAccount(
LPWSTR wTargetComputer, // specifies where to add account
LPWSTR MachineAccount, // name of account
DWORD AccountType // account type
);

BOOL
SetCurrentPrivilege(
LPCTSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
);

void
DisplayError(
LPSTR szAPI, // pointer to failed API name
DWORD dwLastError
);

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

//
// Unicode entry point and argv
//

int
__cdecl
wmain(
int argc,
wchar_t *argv[]
)
{
LPWSTR wMachineAccount;
LPWSTR wPrimaryDC;
LPTSTR MachineAccountPrivilege = SE_MACHINE_ACCOUNT_NAME;
DWORD dwTrustAccountType = UF_WORKSTATION_TRUST_ACCOUNT;
NET_API_STATUS nas;
BOOL bSuccess;

if(argc < 2) {
fprintf(stderr, "Usage: %ls <machineaccountname> [domain]\n",
argv[0]);
return RTN_USAGE;
}

wMachineAccount = argv[1];

//
// if a domain name was specified, fetch the computer name of the
// primary domain controller
//
if(argc == 3) {

nas = NetGetDCName(NULL, argv[2], (LPBYTE *)&wPrimaryDC);

if(nas != NERR_Success) {
DisplayError("NetGetDCName", nas);
return RTN_ERROR;
}
} else {
//
// default will operate on local machine. Non-NULL wPrimaryDC will
// cause buffer to be freed
//
wPrimaryDC = NULL;
}

bSuccess = AddMachineAccount(
wPrimaryDC, // primary DC computer name
wMachineAccount, // computer account name
dwTrustAccountType // computer account type
);

if(!bSuccess && GetLastError() == ERROR_ACCESS_DENIED ) {

//
// try to enable the SeMachineAccountPrivilege
//
if(SetCurrentPrivilege( MachineAccountPrivilege, TRUE )) {

//
// enabled the privilege. retry the add operation
//
bSuccess = AddMachineAccount(
wPrimaryDC,
wMachineAccount,
dwTrustAccountType
);

//
// disable the privilege
//
SetCurrentPrivilege( MachineAccountPrivilege, FALSE );
}
}

//
// free the buffer allocated for the PDC computer name
//
if(wPrimaryDC) NetApiBufferFree(wPrimaryDC);

if(!bSuccess) {
DisplayError("AddMachineAccount", GetLastError());
return RTN_ERROR;
}

return RTN_OK;
}

BOOL
AddMachineAccount(
LPWSTR wTargetComputer,
LPWSTR MachineAccount,
DWORD AccountType
)
{
WCHAR wAccount[MAX_COMPUTERNAME_LENGTH + 2];
LPWSTR wPassword;
USER_INFO_1 ui;
DWORD cchAccount;
DWORD cchLength;
NET_API_STATUS nas;

//
// ensure a valid computer account type was passed
// TODO SetLastError
//
if (AccountType != UF_WORKSTATION_TRUST_ACCOUNT &&
AccountType != UF_SERVER_TRUST_ACCOUNT &&
AccountType != UF_INTERDOMAIN_TRUST_ACCOUNT
) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

//
// obtain number of chars in computer account name
//
cchLength = cchAccount = lstrlenW(MachineAccount);

//
// ensure computer name doesn't exceed maximum length
//
if(cchLength > MAX_COMPUTERNAME_LENGTH) {
SetLastError(ERROR_INVALID_ACCOUNT_NAME);
return FALSE;
}

//
// password is the computer account name converted to lowercase
// convert the passed MachineAccount in place
//
wPassword = MachineAccount;

//
// copy MachineAccount to the wAccount buffer allocated while
// converting computer account name to uppercase.
// convert password (inplace) to lowercase
//
while(cchAccount--) {
wAccount[cchAccount] = towupper( MachineAccount[cchAccount] );
wPassword[cchAccount] = towlower( wPassword[cchAccount] );
}

//
// computer account names have a trailing Unicode '$'
//
wAccount[cchLength] = L'$';
wAccount[cchLength + 1] = L'\0'; // terminate the string

//
// if the password is greater than the max allowed, truncate
//
if(cchLength > LM20_PWLEN) wPassword[LM20_PWLEN] = L'\0';

//
// initialize USER_INFO_x structure
//
ZeroMemory(&ui, sizeof(ui));

ui.usri1_name = wAccount;
ui.usri1_password = wPassword;

ui.usri1_flags = AccountType | UF_SCRIPT;
ui.usri1_priv = USER_PRIV_USER;

nas = NetUserAdd(
wTargetComputer, // target computer name
1, // info level
(LPBYTE) &ui, // buffer
NULL
);

//
// indicate whether it was successful
//
if(nas == NERR_Success) {
return TRUE;
}
else {
SetLastError(nas);
return FALSE;
}
}

BOOL
SetCurrentPrivilege(
LPCTSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
BOOL bSuccess=FALSE;

if(!LookupPrivilegeValue(NULL, Privilege, &luid)) return FALSE;

if(!OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
&hToken
)) return FALSE;

//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;

AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious
);

if(GetLastError() == ERROR_SUCCESS) {
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;

if(bEnablePrivilege) {
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
}
else {
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
tpPrevious.Privileges[0].Attributes);
}

AdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL
);

if(GetLastError() == ERROR_SUCCESS) bSuccess=TRUE;
}

CloseHandle(hToken);

return bSuccess;
}

void
DisplayError(
LPSTR szAPI, // pointer to failed API name
DWORD dwLastError
)
{
HMODULE hModule = NULL;
LPSTR MessageBuffer;
DWORD dwBufferLength;

fprintf(stderr,"%s error! (rc=%lu)\n", szAPI, dwLastError);

if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
hModule = LoadLibraryEx(
TEXT("netmsg.dll"),
NULL,
LOAD_LIBRARY_AS_DATAFILE
);
}

if(dwBufferLength=FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM |
((hModule != NULL) ? FORMAT_MESSAGE_FROM_HMODULE : 0),
hModule, // module to get message from
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPSTR) &MessageBuffer,
0,
NULL
))
{
DWORD dwBytesWritten;

//
// Output message string on stderr
//
WriteFile(
GetStdHandle(STD_ERROR_HANDLE),
MessageBuffer,
dwBufferLength,
&dwBytesWritten,
NULL
);

//
// free the buffer allocated by the system
//
LocalFree(MessageBuffer);
}

if(hModule != NULL)
FreeLibrary(hModule);
}