Hi,
I'm trying to change the password of a user programatically in a
server program (that will eventually be running as a service).
However I am running in to problems. If I call
NetUserChangePassword() I get an error code of 2245 for my troubles,
and I don't know what that corresponds to.
If I do a LogonUser and ImpersonateUser before the
NetUserChangePassword I get error code 5 (ERROR_ACCESS_DENIED).
Can anyone suggest what I might be doing wrong?
Here is a code snippet. #define CASE1 for the first behavior and
CASE2 for the second.
Thanks in advance for any help.
----- code snippet follows--------
char *user, *oldp, *newp;
// make wide char copies of user name and passwords.
// These are required for the password change function.
wchar_t *wuser, *woldp, *wnewp;
wuser = new wchar_t[strlen(user)+1];
woldp = new wchar_t[strlen(oldp)+1];
wnewp = new wchar_t[strlen(newp)+1];
if( !MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, user, -1, wuser, strlen(user)+1))
logger->Trace( TRC_HI, "Couldn't create unicode user name." );
if( !MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, oldp, -1, woldp, strlen(oldp)+1))
logger->Trace( TRC_HI, "Couldn't create unicode oldp." );
if( !MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, newp, -1, wnewp, strlen(newp)+1))
logger->Trace( TRC_HI, "Couldn't create unicode newp." );
#ifdef CASE1
NET_API_STATUS status = NetUserChangePassword( NULL, wuser, woldp, wnewp );
#endif
#ifdef CASE2
// The password change operation is performed by first logging on
// as the user, then impersonating them, then changing the password,
// and then reverting back to self. The impersonation is necessary
// in order to ensure that the user cannot perform operations that
// they would not be allowed to do normally, such as changing a password
// on a locked account, or changing their password when their settings
// explicitly say they can't.
HANDLE usertok; // handle to user auth token
NET_API_STATUS status; // return value for net api functions.
if( !LogonUser( user, NULL, oldp, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &usertok ) ) {
status = GetLastError();
switch( status ) {
case ERROR_PRIVILEGE_NOT_HELD:
logger->Trace( TRC_MED, "Attempting to change password. "
"Need SE_TCB_NAME privelege to perform LogonUser()" );
break;
case ERROR_ACCESS_DENIED:
logger->Trace( TRC_MED, "Attempting to change password. "
"Need SE_CHANGE_NOTIFY_NAME privilege to LogonUser()" );
break;
default:
logger->Trace( TRC_MED, "Attempting to change password. "
"Couldn't logon as user. Error code: %d", GetLastError());
}
} else {
if( !ImpersonateLoggedOnUser( usertok ) ) {
status = GetLastError();
logger->Trace( TRC_MED, "Attempting to change password. "
"Couldn't impersonate user. Error code: %d", status );
} else {
status = NetUserChangePassword( NULL, wuser, woldp, wnewp );
if( status != ERROR_SUCCESS )
logger->Trace( TRC_MED, "Change password failed. Error code: %d", status );
}
if( !RevertToSelf() ) {
status = GetLastError();
logger->Trace( TRC_MED, "Attempting to change password. "
"Couldn't revert to self!!! Error nr: %d", status );
}
CloseHandle( &usertok );
}
#endif
// The microsoft documentation for NetUserChangePassword does not
// tell you the correct return values. These were found by reading
// WINERROR.H and by trying out various cases.
switch( status ) {
case ERROR_SUCCESS:
strcpy( ret_msg.ret_code, "0" );
break;
case ERROR_INVALID_PASSWORD: // This appears to be what is returned if
// the old password is wrong.
case ERROR_WRONG_PASSWORD: // But maybe this could also be returned.
logger->Trace( TRC_MED, "Password change failed - wrong password for user %s",
user );
strcpy( ret_msg.ret_code, "-3" );
break;
case ERROR_ILL_FORMED_PASSWORD:
case ERROR_PASSWORD_RESTRICTION:
logger->Trace( TRC_MED, "Password change failed - ill formed password for user %s",
user );
strcpy( ret_msg.ret_code, "-4" );
break;
case ERROR_NO_SUCH_USER:
logger->Trace( TRC_MED, "Password change failed - userid undefined for user %s",
user );
strcpy( ret_msg.ret_code, "-2" );
break;
case ERROR_ACCOUNT_RESTRICTION:
case ERROR_PASSWORD_EXPIRED:
case ERROR_ACCOUNT_DISABLED:
case ERROR_ACCESS_DENIED:
default:
logger->Trace( TRC_MED, "Password change failed - undefined cause (%d) for user %s",
status, user );
strcpy( ret_msg.ret_code, "-1" );
break;
}
#define NERR_BASE 2100
#define NERR_PasswordTooShort (NERR_BASE+145) /* The password is
shorter than required. */
NERR_PasswordTooShort is one of the error codes that
NetUserChangePassword() can return (from the docs).