How to change a user's password on a remote computer






4.48/5 (8 votes)
Password Changer - A reusable tool for changing a user password's remotely.
- Download project with sources - 29.4 KB
- Download static binary (English) - 109 KB
- Download static binary (German) - 109 KB
Introduction
This is my first article on CodeProject. So please don't expect too much. ;-)
Given scenario
An old workstation running Windows 2000 acts as a "file server". Users can access shared folders over the network by connecting using a username and password. Unfortunately, in a workgroup environment, there is no built-in way to change the passwords of these local users from a remote machine.
Tasks to solve
The described problem can be divided into three main parts:
- How to get a list of all the computers in our workgroup?
- How to get a list of all users from a specific computer?
- How to change a user's password on a remote computer?
The API is your friend
In the MSDN are three corresponding network management functions mentioned. Their usage is well explained, so I won't recite the MSDN.
NetServerEnum
NetQueryDisplayInformation
(see: How to get a list of users from a server)NetUserChangePassword
But the simple NetUserChangePassword
function prototype should be self-explanatory.
DWORD NetUserChangePassword( LPCWSTR domainname, LPCWSTR username, LPCWSTR oldpassword, LPCWSTR newpassword );
Naming conventions
The different strings (e.g., usernames, passwords) are limited to a certain number of characters. It seems that these limitations are a relic from the NT LAN Manager.
Name | Length |
Domain/Workgroup | 15 |
Server | 15 |
User | 20 |
Password | 14 |
Security concerns
NetQueryDisplayInformation
requires anonymous access to the remote machine. Furthermore, an established NULL session connection (e.g., to $IPC) is required.
Using the code
The server and the user enumeration are wrapped into several classes.
Iterating through the result list shouldn't be a big deal.
NetObjectEnumerator *noe = new [ NetServerEnumerator(Domain) | NetUserEnumerator(Server) ] ; if(noe->GetError() == ERROR_SUCCESS){ while(noe->HasNext()){ NetObject *no = noe->GetNext(); ... = no->name; // process the current server/user name... } } else ... = NetObjectEnumerator::GetErrorMessage(noe->GetError()); // process the occurred error...
Error codes and message strings
The FormatMessage
function maps an error code to a message. For network management errors, the netmsg.dll module is used to look up the specified errors.
CString GetErrorMessage(DWORD dwLastError){ HMODULE hModule = NULL; DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM; LPTSTR lpMessageBuffer = NULL; if((dwLastError >= NERR_BASE) && (dwLastError <= MAX_NERR)){ hModule = LoadLibraryEx(_T("netmsg.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE); if(hModule != NULL) dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE; } FormatMessage( dwFormatFlags, hModule, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMessageBuffer, 0, NULL); CString szMessage; szMessage.Format(_T("%s (%u)"), lpMessageBuffer, dwLastError); LocalFree(lpMessageBuffer); if(hModule != NULL) FreeLibrary(hModule); return szMessage; }
Credits
Credits go to Chris Maunder for his great HyperLink static control.
Notice
The application has not been tested in a domain environment. Any volunteers?
Please feel free to send your feedback to me via email. Bug reports, spelling corrections, opinions, and comments would be much appreciated.
History
- 05/16/2006:
- Original article published.
- Version 1.0.0.1 released
The newest Password Changer version is also available on my website.