This sample changes the password for an arbitrary user on an arbitrary
target machine.
When targeting a domain controller for account update operations,
be sure to target the primary domain controller for the domain.
The account settings are replicated by the primary domain controller
to each backup domain controller as appropriate. The NetGetDCName()
function call can be used to get the primary domain controller
computer name from a domain name.
Username is argv[1]
new password is argv[2]
optional target machine (or domain name) is argv[3]
optional old password is argv[4]. This allows non-admin password
changes.
Note that admin or account operator privilege is required on the
target machine unless argv[4] is present and represents the correct
current password.
NetUserSetInfo() at info-level 1003 is appropriate for administrative
override of an existing password.
NetUserChangePassword() allows for an arbitrary user to override
an existing password providing that the current password is confirmed.
//
// obtain target machine name, if appropriate,
// always in Unicode, as that is what the API takes.
//
if(argv[3][0] == L'\\' && argv[3][1] == L'\\') {
//
// target specified machine name
//
wComputerName = argv[3];
}
else {
//
// the user specified a domain name. Look up the PDC.
// This is done in both password change cases to ensure the
// same computer is targeted for the update operation.
//
nas = NetGetDCName(
NULL,
argv[3],
(LPBYTE *)&wComputerName
);
//
// call FormatMessage() to allow for message text to be acquired
// from the system or the supplied module handle.
//
if(dwBufferLength = FormatMessageA(
dwFormatFlags,
hModule, // module to get message from (NULL == system)
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPSTR) &MessageBuffer,
0,
NULL
))
{
DWORD dwBytesWritten;
/* Save HKEY_LOCAL_MACHINE registry key, each subkey saved to a file of
* name subkey
*
* this allows us to get around security restrictions which prevent
* the use of RegSaveKey() on the root key
*
* the optional target machine name is specified in argv[1]
*
* v1.21
* Scott Field (sfield) 01-Apr-1995
*/
int main(int argc, char *argv[])
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
LONG rc; // contains error value returned by Regxxx()
HKEY hKey; // handle to key we are interested in
LPTSTR MachineName=NULL; // pointer to machine name
DWORD dwSubKeyIndex=0; // index into key
char szSubKey[_MAX_FNAME]; // this should be dynamic.
// _MAX_FNAME is good because this
// is what we happen to save the
// subkey as
DWORD dwSubKeyLength=_MAX_FNAME; // length of SubKey buffer
// save registry subkey szSubKey to filename szSubKey
if( (lRetVal=SaveRegistrySubKey(hKey, szSubKey, szSubKey)
) != ERROR_SUCCESS)
{
PERR("SaveRegistrySubKey", lRetVal);
}
// increment index into the key
dwSubKeyIndex++;
// reset buffer size
dwSubKeyLength=_MAX_FNAME;
// Continue the festivities
continue;
}
else
{
//
// note: we need to watch for ERROR_MORE_DATA
// this indicates we need a bigger szSubKey buffer
//
PERR("RegEnumKeyEx", rc);
return RTN_ERROR;
}
} // RegEnumKeyEx
// close registry key we have been working with
RegCloseKey(hKey);
// Revoke all privileges this process holds (including backup)
AdjustTokenPrivileges( hToken, TRUE, NULL, 0, NULL, NULL);
// close handle to process token
CloseHandle(hToken);
return RTN_OK;
}
LONG SaveRegistrySubKey(
HKEY hKey, // handle of key to save
LPTSTR szSubKey, // pointer to subkey name to save
LPTSTR szSaveFileName // pointer to save path/filename
)
{
HKEY hKeyToSave; // Handle of subkey to save
LONG rc; // result code from RegXxx
DWORD dwDisposition;
if((rc=RegCreateKeyEx(hKey,
szSubKey, // Name of subkey to open
0,
NULL,
REG_OPTION_BACKUP_RESTORE, // in winnt.h
KEY_QUERY_VALUE, // minimal access
NULL,
&hKeyToSave,
&dwDisposition)
) == ERROR_SUCCESS)
{
// Save registry subkey. If the registry is remote, files will
// be saved on the remote machine
rc=RegSaveKey(hKeyToSave, szSaveFileName, NULL);
// close registry key we just tried to save
RegCloseKey(hKeyToSave);
}
// return the last registry result code
return rc;
}
void PERR(
LPTSTR szAPI, // pointer to failed API name
DWORD dwLastError // last error value associated with API
)
{
LPTSTR MessageBuffer;
DWORD dwBufferLength;
//
// TODO get this fprintf out of here!
//
fprintf(stderr,"%s error! (rc=%lu)\n", szAPI, dwLastError);
Members
usri1008_flags
Contains values that determine several features. This member can be any of the following values: Value Meaning
UF_SCRIPT The logon script executed. This value must be set for LAN Manager 2.0 or Windows NT.
UF_ACCOUNTDISABLE The user's account is disabled.
UF_HOMEDIR_REQUIRED The home directory is required. This value is ignored in Windows NT.
UF_PASSWD_NOTREQD No password is required.
UF_PASSWD_CANT_CHANGE The user cannot change the password.
UF_LOCKOUT The account is currently locked out. For NetUserSetInfo, this value can be cleared to unlock a previously locked account. This value cannot be used to lock a previously unlocked account.
UF_DONT_EXPIRE_PASSWD Windows NT: Represents the password, which should never expire on the account.
if (argc != 3)
{
fwprintf(stderr, L"Usage: %s \\\\ServerName UserName\n", argv[0]);
exit(1);
}
// Fill in the USER_INFO_1008 structure member.
// UF_SCRIPT: required for LAN Manager 2.0 and
// Windows NT/Windows 2000.
//
ui.usri1008_flags = UF_SCRIPT | UF_ACCOUNTDISABLE;
//
// Call the NetUserSetInfo function
// to disable the account, specifying level 1008.
//
nStatus = NetUserSetInfo(argv[1],
argv[2],
dwLevel,
(LPBYTE)&ui,
NULL);
//
// Display the result of the call.
//
if (nStatus == NERR_Success)
fwprintf(stderr, L"User account %s has been disabled\n", argv[2]);
else
fprintf(stderr, "A system error has occurred: %d\n", nStatus);