windows-nt/Source/XPSP1/NT/mergedcomponents/advapi32/mschapp.c
2020-09-26 16:20:57 +08:00

559 lines
20 KiB
C

/*++
Copyright (C) Microsoft Corporation, 1999
Module Name:
mschapp - MS-CHAP Password Change API
Abstract:
These APIs correspond to the MS-CHAP RFC -2433 sections 9 and 10. In order
to develop an MS-CHAP RAS server that works with an NT domain, these APIs
are required.
The MS-CHAP change password APIs are exposed through a DLL that is obtained
from PSS. This DLL is not distributed with NT4.0 or Win2000. It is up to
the ISV to install this with their product. The DLL name is MSCHAPP.DLL.
Only wide (Unicode) versions of these apis will be available. These are the
2 callable APIs:
* MSChapSrvChangePassword
* MsChapSrvChangePassword2
--*/
#define UNICODE
#define _UNICODE
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <ntsam.h>
#include <ntsamp.h>
#include <ntlsa.h>
#include <mschapp.h>
//////////////////////////////////////////////////////////////
// //
// //
// Exported MSChap change password Functions //
// //
// //
//////////////////////////////////////////////////////////////
//critical section for MSChap change password functions
CRITICAL_SECTION MSChapChangePassword;
//function pointers for MSChap Functions
HINSTANCE hSamlib = NULL;
typedef NTSTATUS(* FNSAMCONNECT)(PUNICODE_STRING,
PSAM_HANDLE,
ACCESS_MASK,
POBJECT_ATTRIBUTES);
typedef NTSTATUS(* FNSAMOPENDOMAIN)(SAM_HANDLE,
ACCESS_MASK,
PSID,
PSAM_HANDLE);
typedef NTSTATUS(* FNSAMLOOKUPNAMESINDOMAIN)(SAM_HANDLE,ULONG,PUNICODE_STRING,
PULONG*,PSID_NAME_USE *);
typedef NTSTATUS(* FNSAMOPENUSER)(SAM_HANDLE,ACCESS_MASK,ULONG,PSAM_HANDLE);
typedef NTSTATUS(* FNSAMICHANGEPASSWORDUSER)(SAM_HANDLE,BOOLEAN,PLM_OWF_PASSWORD,PLM_OWF_PASSWORD,
BOOLEAN,PNT_OWF_PASSWORD,PNT_OWF_PASSWORD);
typedef NTSTATUS(* FNSAMICHANGEPASSWORDUSER2)(PUNICODE_STRING,
PUNICODE_STRING,
PSAMPR_ENCRYPTED_USER_PASSWORD,
PENCRYPTED_NT_OWF_PASSWORD,
BOOLEAN,PSAMPR_ENCRYPTED_USER_PASSWORD,
PENCRYPTED_LM_OWF_PASSWORD);
typedef NTSTATUS(* FNSAMCLOSEHANDLE)(SAM_HANDLE);
typedef NTSTATUS(* FNSAMFREEMEMORY)(PVOID);
FNSAMCONNECT FnSamConnect = NULL;
FNSAMOPENDOMAIN FnSamOpenDomain = NULL;
FNSAMLOOKUPNAMESINDOMAIN FnSamLookupNamesInDomain = NULL;
FNSAMOPENUSER FnSamOpenUser = NULL;
FNSAMICHANGEPASSWORDUSER FnSamiChangePasswordUser = NULL;
FNSAMICHANGEPASSWORDUSER2 FnSamiChangePasswordUser2 = NULL;
FNSAMCLOSEHANDLE FnSamCloseHandle = NULL;
FNSAMFREEMEMORY FnSamFreeMemory = NULL;
/*++
MSChapSrvChangePassword:
Changes the password of a user account. Password will be set to
NewPassword only if OldPassword matches the current user password for this
user and there are no restrictions on using the new password. This call
allows users to change their own password if they have access
USER_CHANGE_PASSWORD. Password update restrictions apply.
Arguments:
ServerName - The server to operate on, or NULL for this machine.
UserName - Name of user whose password is to be changed
LMOldPresent - TRUE if the LmOldOwfPassword is valid. This should only be
FALSE if the old password is too long to be represented by a LM
password (Complex NT password). Note the LMNewOwfPassword must always
be valid. If the new password is complex, the LMNewOwfPassword should
be the well-known LM OWF of a NULL password.
LmOldOwfPassword - One-way-function of the current LM password for the
user. Ignored if LmOldPresent == FALSE
LmNewOwfPassword - One-way-function of the new LM password for the user.
NtOldOwfPassword - One-way-function of the current NT password for the
user.
NtNewOwfPassword - One-way-function of the new NT password for the user.
Return Value:
STATUS_SUCCESS - The Service completed successfully.
STATUS_ACCESS_DENIED - Caller does not have the appropriate access to
complete the operation.
STATUS_INVALID_HANDLE - The supplied server or username was not valid.
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, e.g.
contains characters that can't be entered from the keyboard, etc.
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password from
being changed. This may be for a number of reasons, including time
restrictions on how often a password may be changed or length
restrictions on the provided password. This error might also be
returned if the new password matched a password in the recent history
log for the account. Security administrators indicate how many of the
most recently used passwords may not be re-used. These are kept in
the password recent history log.
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's current
password.
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct
state (disabled or enabled) to perform the requested operation. The
domain server must be enabled for this operation
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect
role (primary or backup) to perform the requested operation.
STATUS_INVALID_PARAMETER_MIX - LmOldPresent or NtPresent or both must be
TRUE.
--*/
WINADVAPI DWORD WINAPI
MSChapSrvChangePassword(
IN LPWSTR ServerName,
IN LPWSTR UserName,
IN BOOLEAN LmOldPresent,
IN PLM_OWF_PASSWORD LmOldOwfPassword,
IN PLM_OWF_PASSWORD LmNewOwfPassword,
IN PNT_OWF_PASSWORD NtOldOwfPassword,
IN PNT_OWF_PASSWORD NtNewOwfPassword)
{
NTSTATUS Status=STATUS_SUCCESS;
DWORD WinErr=ERROR_SUCCESS;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING UnicodeName;
SAM_HANDLE SamHandle = NULL;
SAM_HANDLE DomainHandle = NULL;
SAM_HANDLE UserHandle = NULL;
LSA_HANDLE LsaHandle = NULL;
PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL;
PULONG RelativeIds = NULL;
PSID_NAME_USE Use = NULL;
if (NULL == UserName || NULL == LmOldOwfPassword || NULL == LmNewOwfPassword ||
NULL == NtOldOwfPassword || NULL == NtNewOwfPassword) {
WinErr = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Initialization.
//
if ( hSamlib == NULL )
{
RtlEnterCriticalSection( &MSChapChangePassword );
if ( hSamlib == NULL )
{
hSamlib = LoadLibrary(L"samlib.dll");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
if (hSamlib != NULL) {
FnSamConnect = (FNSAMCONNECT) GetProcAddress(hSamlib,
"SamConnect");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamOpenDomain = (FNSAMOPENDOMAIN) GetProcAddress(hSamlib,
"SamOpenDomain");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamLookupNamesInDomain = (FNSAMLOOKUPNAMESINDOMAIN) GetProcAddress(hSamlib,
"SamLookupNamesInDomain");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamOpenUser = (FNSAMOPENUSER) GetProcAddress(hSamlib,
"SamOpenUser");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamCloseHandle = (FNSAMCLOSEHANDLE) GetProcAddress(hSamlib,
"SamCloseHandle");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamFreeMemory = (FNSAMFREEMEMORY) GetProcAddress(hSamlib,
"SamFreeMemory");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamiChangePasswordUser = (FNSAMICHANGEPASSWORDUSER) GetProcAddress(hSamlib,
"SamiChangePasswordUser");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamiChangePasswordUser2 = (FNSAMICHANGEPASSWORDUSER2) GetProcAddress(hSamlib,
"SamiChangePasswordUser2");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
}
}
RtlLeaveCriticalSection( &MSChapChangePassword );
}
RtlInitUnicodeString(&UnicodeName, ServerName);
InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
//
// Connect to the LSA on the server
//
Status = LsaOpenPolicy(
&UnicodeName,
&oa,
POLICY_VIEW_LOCAL_INFORMATION,
&LsaHandle);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = LsaQueryInformationPolicy(
LsaHandle,
PolicyAccountDomainInformation,
(PVOID *)&DomainInfo);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = FnSamConnect(
&UnicodeName,
&SamHandle,
SAM_SERVER_LOOKUP_DOMAIN,
&oa);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = FnSamOpenDomain(
SamHandle,
DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS | DOMAIN_READ_PASSWORD_PARAMETERS,
DomainInfo->DomainSid,
&DomainHandle);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
RtlInitUnicodeString(
&UnicodeName,
UserName);
Status = FnSamLookupNamesInDomain(
DomainHandle,
1,
&UnicodeName,
&RelativeIds,
&Use);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
if (Use[0] != SidTypeUser)
{
WinErr = ERROR_INVALID_SID;
goto Cleanup;
}
Status = FnSamOpenUser(
DomainHandle,
USER_CHANGE_PASSWORD,
RelativeIds[0],
&UserHandle);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = FnSamiChangePasswordUser(
UserHandle,
LmOldPresent, // Only false if Old password too complex
LmOldOwfPassword,
LmNewOwfPassword,
TRUE, // NT password present
NtOldOwfPassword,
NtNewOwfPassword);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Cleanup:
if (LsaHandle != NULL)
{
LsaClose(LsaHandle);
}
if (UserHandle != NULL)
{
FnSamCloseHandle(UserHandle);
}
if (DomainHandle != NULL)
{
FnSamCloseHandle(DomainHandle);
}
if (SamHandle != NULL)
{
FnSamCloseHandle(SamHandle);
}
if (DomainInfo != NULL)
{
LsaFreeMemory(DomainInfo);
}
if (RelativeIds != NULL)
{
FnSamFreeMemory(RelativeIds);
}
if (Use != NULL)
{
FnSamFreeMemory(Use);
}
if (ERROR_SUCCESS != WinErr) {
return WinErr;
}
return RtlNtStatusToDosError(Status);
}
/*++
MSChapSrvChangePassword2:
Changes the password of a user account. Password will be set to
NewPassword only if OldPassword matches the current user password for this
user and there are no restrictions on using the new password. This call
allows users to change their own password if they have access
USER_CHANGE_PASSWORD. Password update restrictions apply.
Arguments:
ServerName - The server to operate on, or NULL for this machine.
UserName - Name of user whose password is to be changed
NewPasswordEncryptedWithOldNt - The new cleartext password encrypted with
the old NT OWF password.
OldNtOwfPasswordEncryptedWithNewNt - The old NT OWF password encrypted
with the new NT OWF password.
LmPresent - If TRUE, indicates that the following two last parameter was
encrypted with the LM OWF password not the NT OWF password.
NewPasswordEncryptedWithOldLm - The new cleartext password encrypted with
the old LM OWF password.
OldLmOwfPasswordEncryptedWithNewLmOrNt - The old LM OWF password encrypted
with the new LM OWF password.
Return Value:
STATUS_SUCCESS - The Service completed successfully.
STATUS_ACCESS_DENIED - Caller does not have the appropriate access to
complete the operation.
STATUS_INVALID_HANDLE - The supplied server or username was not valid.
STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, e.g.
contains characters that can't be entered from the keyboard, etc.
STATUS_PASSWORD_RESTRICTION - A restriction prevents the password from
being changed. This may be for a number of reasons, including time
restrictions on how often a password may be changed or length
restrictions on the provided password. This error might also be
returned if the new password matched a password in the recent history
log for the account. Security administrators indicate how many of the
most recently used passwords may not be re-used. These are kept in
the password recent history log.
STATUS_WRONG_PASSWORD - OldPassword does not contain the user's current
password.
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct
state (disabled or enabled) to perform the requested operation. The
domain server must be enabled for this operation.
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect
role (primary or backup) to perform the requested operation.
--*/
WINADVAPI DWORD WINAPI
MSChapSrvChangePassword2(
IN LPWSTR ServerName,
IN LPWSTR UserName,
IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt,
IN PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt,
IN BOOLEAN LmPresent,
IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLmOrNt)
{
UNICODE_STRING UnicodeServer;
UNICODE_STRING UnicodeUser;
DWORD WinErr = ERROR_SUCCESS;
if (NULL == UserName || NULL == NewPasswordEncryptedWithOldNt ||
NULL == NewPasswordEncryptedWithOldLm || NULL ==OldNtOwfPasswordEncryptedWithNewNt ||
NULL == OldLmOwfPasswordEncryptedWithNewLmOrNt) {
WinErr = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Initialization.
//
if ( hSamlib == NULL )
{
RtlEnterCriticalSection( &MSChapChangePassword );
if ( hSamlib == NULL )
{
hSamlib = LoadLibrary(L"samlib.dll");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
if (hSamlib != NULL) {
FnSamConnect = (FNSAMCONNECT) GetProcAddress(hSamlib,
"SamConnect");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamOpenDomain = (FNSAMOPENDOMAIN) GetProcAddress(hSamlib,
"SamOpenDomain");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamLookupNamesInDomain = (FNSAMLOOKUPNAMESINDOMAIN) GetProcAddress(hSamlib,
"SamLookupNamesInDomain");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamOpenUser = (FNSAMOPENUSER) GetProcAddress(hSamlib,
"SamOpenUser");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamCloseHandle = (FNSAMCLOSEHANDLE) GetProcAddress(hSamlib,
"SamCloseHandle");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamFreeMemory = (FNSAMFREEMEMORY) GetProcAddress(hSamlib,
"SamFreeMemory");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamiChangePasswordUser = (FNSAMICHANGEPASSWORDUSER) GetProcAddress(hSamlib,
"SamiChangePasswordUser");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
FnSamiChangePasswordUser2 = (FNSAMICHANGEPASSWORDUSER2) GetProcAddress(hSamlib,
"SamiChangePasswordUser2");
WinErr = GetLastError();
if (ERROR_SUCCESS != WinErr) {
goto Cleanup;
}
}
}
RtlLeaveCriticalSection( &MSChapChangePassword );
}
RtlInitUnicodeString(&UnicodeServer, ServerName);
RtlInitUnicodeString(&UnicodeUser, UserName);
return RtlNtStatusToDosError(FnSamiChangePasswordUser2(&UnicodeServer,
&UnicodeUser,
NewPasswordEncryptedWithOldNt,
OldNtOwfPasswordEncryptedWithNewNt,
LmPresent,
NewPasswordEncryptedWithOldLm,
OldLmOwfPasswordEncryptedWithNewLmOrNt));
Cleanup:
return WinErr;
}