HOWTO: Manage Computer Accounts Programmatically in Windows NT
ID: Q136867
|
The information in this article applies to:
-
Microsoft Win32 Software Development Kit (SDK)
SUMMARY
The ability to manage Windows NT computer accounts can be accomplished by
using the Windows NT server manager utility. The server manager (srvmgr)
can be used to create and delete various computer accounts, including the
following account types:
- Windows NT Workstation
- Windows NT Server (non domain controller)
- Windows NT Backup domain controller
Furthermore, Windows NT user manager for domains (usrmgr) provides the
ability to manage trust relationships with domains. The management of
interdomain trust accounts "permitted to trust this domain" will be
addressed by this article.
The list of trusted domains can be managed programmatically with the
Windows NT LSA (local security authority) API. This procedure will not be
discussed in this article.
This article describes how to manage Windows NT computer accounts
programmatically by using Win32 API calls from Windows NT. It is assumed
that you already have an understanding of the various computer account
types in Windows NT.
MORE INFORMATION
Considerations for Managing Computer Accounts
- Computer account management should take place on the primary domain
controller for the target domain.
- The computer account name should be all uppercase for consistency with
Windows NT account management utilities.
- A computer account name always has a trailing dollar sign ($). Any APIs
used to manage computer accounts must build the computer name such that
the last character of the computer account name is a dollar sign ($).
For interdomain trust, the account name is TrustingDomainName$.
- The maximum computer name length is MAX_COMPUTERNAME_LENGTH (15). This
length does not include the trailing dollar sign ($).
- The password for a new computer account should be the lowercase
representation of the computer account name, without the trailing dollar
sign ($). For interdomain trust, the password can be an arbitrary value
that matches the value specified on the trust side of the relationship.
- The maximum password length is LM20_PWLEN (14). The password should be
truncated to this length if the computer account name exceeds this
length.
- The password provided at computer-account-creation time is valid only
until the computer account becomes active on the domain. A new password
is established during trust relationship activation.
- The user that calls the account management functions must have
Administrator privilege on the target computer. In the case of existing
computer accounts, the creator of the account can manage the account,
regardless of administrative membership.
- The SeMachineAccountPrivilege can be granted on the target computer to
give specified users the ability to create computer accounts. This gives
non-administrators the ability to create computer accounts. The caller
needs to enable this privilege prior to adding the computer account.
- The Net function calls are currently implemented as Unicode only.
The caller must insure that strings passed to these functions are
in Unicode form.
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)
The following list of APIs can be used to manage computer accounts in
the target domain:
- NetGetDCName can be used to obtain the computer name of the primary
domain controller.
- NetUserAdd can be used for creating a new computer account.
- NetUserDel can be used to delete an existing computer account.
- NetUserSetInfo can be used to modify the password of an existing
computer account. This is useful for resetting a computer account to a
known state.
- NetUserEnum can be used to enumerate existing computer accounts. This
API can return a list of accounts based on account type through the use
of the filter parameter.
Sample Code
// works compiled ansi or unicode
#define UNICODE
#define _UNICODE
#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13
/*++
The following sample code adds the specified workstation computer account
to the specified domain. If no domain is specified, the computer account is
created on the local computer.
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.
The following import libraries are required:
netapi32.lib
advapi32.lib
#include <windows.h>
#include <stdio.h>
#include <lm.h>
BOOL
AddMachineAccount(
LPWSTR wTargetComputer,
LPWSTR MachineAccount,
DWORD AccountType
);
BOOL SetCurrentPrivilege(
LPWSTR TargetComputer, // target of privilege operation
LPCWSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
);
int wmain(int argc, wchar_t *argv[])
{
LPWSTR wMachineAccount;
LPWSTR wPrimaryDC;
LPWSTR wMachineAccountPrivilege = L"SeMachineAccountPrivilege";
DWORD dwError;
BOOL bSuccess;
if (argc < 2)
{
fprintf(stderr, "Usage: %ls <machineaccountname> [domain]\n",
argv[0]);
return RTN_USAGE;
}
wMachineAccount = argv[1];
//
// default will operate on local machine. Non-NULL wPrimaryDC will
// cause buffer to be freed
//
wPrimaryDC = NULL;
//
// if a domain name was specified, fetch the computer name of the
// primary domain controller
//
if (argc == 3) {
dwError = NetGetDCName(NULL, argv[2], (LPBYTE *)&wPrimaryDC);
if(dwError != NO_ERROR) {
fprintf(stderr,"NetGetDCName error! (rc=%lu)\n", dwError);
return RTN_ERROR;
}
}
bSuccess=AddMachineAccount(
wPrimaryDC, // primary DC computer name
wMachineAccount, // computer account name
UF_WORKSTATION_TRUST_ACCOUNT // computer account type
);
if(!bSuccess && GetLastError() == ERROR_ACCESS_DENIED ) {
//
// try to enable the SeMachineAccountPrivilege
//
if(SetCurrentPrivilege(
wPrimaryDC, wMachineAccountPrivilege, TRUE )) {
//
// enabled the privilege. retry the add operation
//
bSuccess=AddMachineAccount(
wPrimaryDC,
wMachineAccount,
UF_WORKSTATION_TRUST_ACCOUNT
);
//
// disable the privilege
//
SetCurrentPrivilege(
wPrimaryDC, wMachineAccountPrivilege, FALSE);
}
}
//
// free the buffer allocated for the PDC computer name
//
if(wPrimaryDC) NetApiBufferFree(wPrimaryDC);
if(!bSuccess)
{
fprintf(stderr,"AddMachineAccount error! (rc=%lu)\n",
GetLastError());
return RTN_ERROR;
}
return RTN_OK;
}
BOOL
AddMachineAccount(
LPWSTR wTargetComputer,
LPWSTR MachineAccount,
DWORD AccountType
)
{
LPWSTR wAccount;
LPWSTR wPassword;
USER_INFO_1 ui;
DWORD cbAccount;
DWORD cbLength;
DWORD dwError;
//
// 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
//
cbLength = cbAccount = lstrlenW(MachineAccount);
//
// ensure computer name doesn't exceed maximum length
//
if(cbLength > MAX_COMPUTERNAME_LENGTH) {
SetLastError(ERROR_INVALID_ACCOUNT_NAME);
return FALSE;
}
//
// allocate storage to contain Unicode representation of
// computer account name + trailing $ + NULL
//
wAccount=(LPWSTR)HeapAlloc(GetProcessHeap(), 0,
(cbAccount + 1 + 1) * sizeof(WCHAR) // Account + '$' + NULL
);
if(wAccount == NULL) return FALSE;
//
// password is the computer account name converted to lowercase
// you will 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(cbAccount--) {
wAccount[cbAccount] = towupper( MachineAccount[cbAccount] );
wPassword[cbAccount] = towlower( wPassword[cbAccount] );
}
//
// computer account names have a trailing Unicode '$'
//
wAccount[cbLength] = L'$';
wAccount[cbLength + 1] = L'\0'; // terminate the string
//
// if the password is greater than the max allowed, truncate
//
if(cbLength > 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;
dwError=NetUserAdd(
wTargetComputer, // target computer name
1, // info level
(LPBYTE) &ui, // buffer
NULL
);
//
// free allocated memory
//
if(wAccount) HeapFree(GetProcessHeap(), 0, wAccount);
//
// indicate whether it was successful
//
if(dwError == NO_ERROR)
return TRUE;
else {
SetLastError(dwError);
return FALSE;
}
}
BOOL SetCurrentPrivilege(
LPWSTR TargetComputer, // target of privilege operation
LPCWSTR 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(!LookupPrivilegeValueW(TargetComputer, 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;
}
Additional query words:
account server workstation trust
Keywords : kbnetwork kbKernBase kbLSA kbNTOS351 kbNTOS400 kbWinOS2000 kbSDKPlatform kbSecurity kbNetAPI kbDSupport kbCodeSam kbGrpKernBase
Version : WINDOWS:
Platform : WINDOWS
Issue type : kbhowto