You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
724 lines
21 KiB
724 lines
21 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ChangePw.c
|
|
|
|
Abstract:
|
|
|
|
This module implements password change from downlevel clients.
|
|
XsChangePasswordSam is called by XsNetUserPasswordSet2 in
|
|
apiuser.c. I've put this in a seperate file because it #includes
|
|
a private SAM header.
|
|
|
|
Author:
|
|
|
|
Dave Hart (davehart) 31-Apr-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "xactsrvp.h"
|
|
#include <ntlsa.h>
|
|
#include <ntsam.h>
|
|
#include <ntsamp.h>
|
|
#include <crypt.h>
|
|
#include <lmcons.h>
|
|
#include "changepw.h"
|
|
#include <netlibnt.h>
|
|
#include <smbgtpt.h>
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Internal function prototyptes. //
|
|
// //
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS
|
|
RtlGetPrimaryDomain(
|
|
IN ULONG SidLength,
|
|
OUT PBOOLEAN PrimaryDomainPresent,
|
|
OUT PUNICODE_STRING PrimaryDomainName,
|
|
OUT PUSHORT RequiredNameLength,
|
|
OUT PSID PrimaryDomainSid OPTIONAL,
|
|
OUT PULONG RequiredSidLength
|
|
);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Exported functions. //
|
|
// //
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
NET_API_STATUS
|
|
XsChangePasswordSam (
|
|
IN PUNICODE_STRING UserName,
|
|
IN PVOID OldPassword,
|
|
IN PVOID NewPassword,
|
|
IN BOOLEAN Encrypted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by XsNetUserPasswordSet2 to change the password
|
|
on a Windows NT machine. The code is based on
|
|
lsa\msv1_0\nlmain.c MspChangePasswordSam.
|
|
|
|
Arguments:
|
|
|
|
UserName - Name of the user to change password for.
|
|
|
|
OldPassword - Old password encrypted using new password as key.
|
|
|
|
NewPassword - New password encrypted using old password as key.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
NT_PRODUCT_TYPE NtProductType;
|
|
UNICODE_STRING DomainName;
|
|
LPWSTR serverName = NULL;
|
|
BOOLEAN DomainNameAllocated;
|
|
BOOLEAN PrimaryDomainPresent;
|
|
USHORT RequiredDomainNameLength;
|
|
ULONG RequiredDomainSidLength;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQos;
|
|
PSID DomainSid = NULL;
|
|
PULONG UserId = NULL;
|
|
PSID_NAME_USE NameUse = NULL;
|
|
SAM_HANDLE SamHandle = NULL;
|
|
SAM_HANDLE DomainHandle = NULL;
|
|
SAM_HANDLE UserHandle = NULL;
|
|
HANDLE OpenedToken;
|
|
|
|
//
|
|
// We're going to _open the local account domain. The name of this
|
|
// domain is "Account" on a WinNT machine, or the name of the
|
|
// primary domain on a LanManNT machine. Figure out the product
|
|
// type, assuming WinNT if RtlGetNtProductType fails.
|
|
//
|
|
|
|
DomainName.MaximumLength = 0;
|
|
DomainName.Buffer = NULL;
|
|
DomainNameAllocated = FALSE;
|
|
|
|
NtProductType = NtProductWinNt;
|
|
|
|
RtlGetNtProductType(
|
|
&NtProductType
|
|
);
|
|
|
|
if (NtProductLanManNt != NtProductType) {
|
|
|
|
NET_API_STATUS error;
|
|
|
|
//
|
|
// The server name is the database name.
|
|
//
|
|
|
|
error = NetpGetComputerName( &serverName );
|
|
|
|
if ( error != NO_ERROR ) {
|
|
return(error);
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
&DomainName,
|
|
serverName
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a LanManNT machine, so we need to find out the
|
|
// name of the primary domain. First get the length of the
|
|
// domain name, then make room for it and retrieve it.
|
|
//
|
|
|
|
Status = RtlGetPrimaryDomain(
|
|
0,
|
|
&PrimaryDomainPresent,
|
|
&DomainName,
|
|
&RequiredDomainNameLength,
|
|
NULL,
|
|
&RequiredDomainSidLength
|
|
);
|
|
|
|
if (STATUS_BUFFER_TOO_SMALL != Status && !NT_SUCCESS(Status)) {
|
|
|
|
KdPrint(("XsChangePasswordSam: Unable to size primary "
|
|
" domain name buffer, %8.8x\n", Status));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
DomainName.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap(), 0,
|
|
DomainName.MaximumLength = RequiredDomainNameLength
|
|
);
|
|
DomainNameAllocated = TRUE;
|
|
|
|
DomainSid = RtlAllocateHeap(
|
|
RtlProcessHeap(), 0,
|
|
RequiredDomainSidLength
|
|
);
|
|
|
|
if (!DomainName.Buffer || !DomainSid) {
|
|
KdPrint(("XsChangePasswordSam: Out of memory allocating %d and %d bytes.",
|
|
RequiredDomainNameLength, RequiredDomainSidLength));
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlGetPrimaryDomain(
|
|
RequiredDomainSidLength,
|
|
&PrimaryDomainPresent,
|
|
&DomainName,
|
|
&RequiredDomainNameLength,
|
|
DomainSid,
|
|
&RequiredDomainSidLength
|
|
);
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0, DomainSid);
|
|
DomainSid = NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("XsChangePasswordSam: Unable to retrieve domain "
|
|
"name, %8.8x\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(PrimaryDomainPresent);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Wrap an exception handler around this entire function,
|
|
// since RPC raises exceptions to return errors.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Connect to local SAM.
|
|
//
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
|
|
ObjectAttributes.SecurityQualityOfService = &SecurityQos;
|
|
|
|
SecurityQos.Length = sizeof(SecurityQos);
|
|
SecurityQos.ImpersonationLevel = SecurityIdentification;
|
|
SecurityQos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
|
|
SecurityQos.EffectiveOnly = FALSE;
|
|
|
|
Status = SamConnect(
|
|
NULL,
|
|
&SamHandle,
|
|
GENERIC_EXECUTE,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(("XsChangePasswordSam: SamConnect failed, status %8.8x\n",
|
|
Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Lookup the Domain SID.
|
|
//
|
|
|
|
Status = SamLookupDomainInSamServer(
|
|
SamHandle,
|
|
&DomainName,
|
|
&DomainSid
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(("XsChangePasswordSam: Cannot find domain %wZ, "
|
|
"status %8.8x\n", &DomainName, Status));
|
|
|
|
Status = STATUS_CANT_ACCESS_DOMAIN_INFO;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Revert to Local System
|
|
//
|
|
Status = NtOpenThreadToken(
|
|
NtCurrentThread(),
|
|
MAXIMUM_ALLOWED,
|
|
TRUE,
|
|
&OpenedToken
|
|
);
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
RevertToSelf();
|
|
|
|
//
|
|
// Open the account domain.
|
|
//
|
|
|
|
Status = SamOpenDomain(
|
|
SamHandle,
|
|
GENERIC_EXECUTE,
|
|
DomainSid,
|
|
&DomainHandle
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
#if DBG
|
|
UNICODE_STRING UnicodeSid;
|
|
|
|
RtlConvertSidToUnicodeString(
|
|
&UnicodeSid,
|
|
DomainSid,
|
|
TRUE
|
|
);
|
|
KdPrint(("XsChangePasswordSam: Cannot open domain %wZ, status %8.8x, SAM handle %8.8x, Domain SID %wZ\n",
|
|
&DomainName, Status, SamHandle, UnicodeSid));
|
|
RtlFreeUnicodeString(&UnicodeSid);
|
|
#endif
|
|
Status = STATUS_CANT_ACCESS_DOMAIN_INFO;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Find the ID for this username.
|
|
//
|
|
|
|
Status = SamLookupNamesInDomain(
|
|
DomainHandle,
|
|
1,
|
|
UserName,
|
|
&UserId,
|
|
&NameUse
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(("XsChangePasswordSam: Cannot lookup user %wZ, "
|
|
"status %8.8x\n", UserName, Status));
|
|
if (STATUS_NONE_MAPPED == Status) {
|
|
Status = STATUS_NO_SUCH_USER;
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Re-impersonate the client
|
|
//
|
|
Status = NtSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
&OpenedToken,
|
|
sizeof( OpenedToken )
|
|
);
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Open the user object.
|
|
//
|
|
|
|
Status = SamOpenUser(
|
|
DomainHandle,
|
|
USER_CHANGE_PASSWORD,
|
|
*UserId,
|
|
&UserHandle
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(("XsChangePasswordSam: Cannot open user %wZ, "
|
|
"status %8.8x\n", UserName, Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (Encrypted) {
|
|
|
|
//
|
|
// The client is Windows for Workgroups, OS/2, or DOS running
|
|
// the ENCRYPT service. Pass the cross-encrypted passwords
|
|
// to SamiLmChangePasswordUser.
|
|
//
|
|
|
|
Status = SamiLmChangePasswordUser(
|
|
UserHandle,
|
|
OldPassword,
|
|
NewPassword
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// The client is DOS not running the ENCRYPT service, and so
|
|
// sent plaintext. Calculate the one-way functions and call
|
|
// SamiChangePasswordUser.
|
|
//
|
|
|
|
LM_OWF_PASSWORD OldLmOwfPassword, NewLmOwfPassword;
|
|
|
|
Status = RtlCalculateLmOwfPassword(
|
|
OldPassword,
|
|
&OldLmOwfPassword
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = RtlCalculateLmOwfPassword(
|
|
NewPassword,
|
|
&NewLmOwfPassword
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("XsChangePasswordSam: Unable to generate OWF "
|
|
"passwords, %8.8x\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Ask SAM to change the LM password and not store a new
|
|
// NT password.
|
|
//
|
|
|
|
Status = SamiChangePasswordUser(
|
|
UserHandle,
|
|
TRUE,
|
|
&OldLmOwfPassword,
|
|
&NewLmOwfPassword,
|
|
FALSE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(("XsChangePasswordSam: Cannot change password "
|
|
"for %wZ, status %8.8x\n", UserName, Status));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
} except (Status = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
KdPrint(("XsChangePasswordSam: caught exception 0x%8.8x\n", Status));
|
|
|
|
if (RPC_S_SERVER_UNAVAILABLE == Status) {
|
|
Status = STATUS_CANT_ACCESS_DOMAIN_INFO;
|
|
}
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
NetApiBufferFree( serverName );
|
|
|
|
if (DomainNameAllocated && DomainName.Buffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, DomainName.Buffer);
|
|
}
|
|
|
|
if (DomainSid) {
|
|
SamFreeMemory(DomainSid);
|
|
}
|
|
|
|
if (UserId) {
|
|
SamFreeMemory(UserId);
|
|
}
|
|
|
|
if (NameUse) {
|
|
SamFreeMemory(NameUse);
|
|
}
|
|
|
|
if (UserHandle) {
|
|
SamCloseHandle(UserHandle);
|
|
}
|
|
|
|
if (DomainHandle) {
|
|
SamCloseHandle(DomainHandle);
|
|
}
|
|
|
|
if (SamHandle) {
|
|
SamCloseHandle(SamHandle);
|
|
}
|
|
|
|
return RtlNtStatusToDosError(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
XsSamOEMChangePasswordUser2_P (
|
|
API_HANDLER_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles a call to SamrOemChangePasswordUser2 coming in
|
|
from Win 95 clients
|
|
|
|
Arguments:
|
|
|
|
API_HANDLER_PARAMETERS - information about the API call. See
|
|
XsTypes.h for details.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS or reason for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PXS_SAMOEMCHGPASSWORDUSER2_P parameters = Parameters;
|
|
STRING UserName;
|
|
SAMPR_ENCRYPTED_USER_PASSWORD EncryptedUserPassword;
|
|
ENCRYPTED_LM_OWF_PASSWORD EncryptedOwfPassword;
|
|
NTSTATUS ntstatus;
|
|
|
|
API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings
|
|
|
|
try {
|
|
if( SmbGetUshort( ¶meters->BufLen ) !=
|
|
sizeof( EncryptedUserPassword) + sizeof( EncryptedOwfPassword ) ) {
|
|
|
|
Header->Status = ERROR_INVALID_PARAMETER;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlCopyMemory( &EncryptedUserPassword,
|
|
parameters->Buffer,
|
|
sizeof( EncryptedUserPassword ) );
|
|
|
|
RtlCopyMemory( &EncryptedOwfPassword,
|
|
parameters->Buffer + sizeof( EncryptedUserPassword ),
|
|
sizeof( EncryptedOwfPassword ) );
|
|
|
|
UserName.Buffer = parameters->UserName;
|
|
UserName.Length = (USHORT) strlen( UserName.Buffer );
|
|
UserName.MaximumLength = UserName.Length;
|
|
|
|
ntstatus = SamiOemChangePasswordUser2(
|
|
NULL,
|
|
&UserName,
|
|
&EncryptedUserPassword,
|
|
&EncryptedOwfPassword );
|
|
|
|
|
|
if( ntstatus == STATUS_NOT_SUPPORTED ) {
|
|
Header->Status = NERR_InvalidAPI;
|
|
} else {
|
|
Header->Status = (WORD)NetpNtStatusToApiStatus( ntstatus );
|
|
}
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // XsSamOEMChangePasswordUser2_P
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Internal function implementation. //
|
|
// //
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Copied from ntos\dll\seurtl.c, where it is disabled. Remove if
|
|
// it is enabled in ntdll.
|
|
//
|
|
|
|
NTSTATUS
|
|
RtlGetPrimaryDomain(
|
|
IN ULONG SidLength,
|
|
OUT PBOOLEAN PrimaryDomainPresent,
|
|
OUT PUNICODE_STRING PrimaryDomainName,
|
|
OUT PUSHORT RequiredNameLength,
|
|
OUT PSID PrimaryDomainSid OPTIONAL,
|
|
OUT PULONG RequiredSidLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure opens the LSA policy object and retrieves
|
|
the primary domain information for this machine.
|
|
|
|
Arguments:
|
|
|
|
SidLength - Specifies the length of the PrimaryDomainSid
|
|
parameter.
|
|
|
|
PrimaryDomainPresent - Receives a boolean value indicating
|
|
whether this machine has a primary domain or not. TRUE
|
|
indicates the machine does have a primary domain. FALSE
|
|
indicates the machine does not.
|
|
|
|
PrimaryDomainName - Points to the unicode string to receive
|
|
the primary domain name. This parameter will only be
|
|
used if there is a primary domain.
|
|
|
|
RequiredNameLength - Recevies the length of the primary
|
|
domain name (in bytes). This parameter will only be
|
|
used if there is a primary domain.
|
|
|
|
PrimaryDomainSid - This optional parameter, if present,
|
|
points to a buffer to receive the primary domain's
|
|
SID. This parameter will only be used if there is a
|
|
primary domain.
|
|
|
|
RequiredSidLength - Recevies the length of the primary
|
|
domain SID (in bytes). This parameter will only be
|
|
used if there is a primary domain.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The requested information has been retrieved.
|
|
|
|
STATUS_BUFFER_TOO_SMALL - One of the return buffers was not
|
|
large enough to receive the corresponding information.
|
|
The RequiredNameLength and RequiredSidLength parameter
|
|
values have been set to indicate the needed length.
|
|
|
|
Other status values as may be returned by:
|
|
|
|
LsaOpenPolicy()
|
|
LsaQueryInformationPolicy()
|
|
RtlCopySid()
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status, IgnoreStatus;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
LSA_HANDLE LsaHandle;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
|
|
PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo;
|
|
|
|
|
|
//
|
|
// Set up the Security Quality Of Service
|
|
//
|
|
|
|
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
|
|
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
SecurityQualityOfService.EffectiveOnly = FALSE;
|
|
|
|
//
|
|
// Set up the object attributes to open the Lsa policy object
|
|
//
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
NULL,
|
|
0L,
|
|
(HANDLE)NULL,
|
|
NULL);
|
|
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
|
|
|
|
//
|
|
// Open the local LSA policy object
|
|
//
|
|
|
|
Status = LsaOpenPolicy( NULL,
|
|
&ObjectAttributes,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
&LsaHandle
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Get the primary domain info
|
|
//
|
|
Status = LsaQueryInformationPolicy(LsaHandle,
|
|
PolicyPrimaryDomainInformation,
|
|
(PVOID *)&PrimaryDomainInfo);
|
|
IgnoreStatus = LsaClose(LsaHandle);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Is there a primary domain?
|
|
//
|
|
|
|
if (PrimaryDomainInfo->Sid != NULL) {
|
|
|
|
//
|
|
// Yes
|
|
//
|
|
|
|
(*PrimaryDomainPresent) = TRUE;
|
|
(*RequiredNameLength) = PrimaryDomainInfo->Name.Length;
|
|
(*RequiredSidLength) = RtlLengthSid(PrimaryDomainInfo->Sid);
|
|
|
|
|
|
|
|
//
|
|
// Copy the name
|
|
//
|
|
|
|
if (PrimaryDomainName->MaximumLength >=
|
|
PrimaryDomainInfo->Name.Length) {
|
|
RtlCopyUnicodeString(
|
|
PrimaryDomainName,
|
|
&PrimaryDomainInfo->Name
|
|
);
|
|
} else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the SID (if appropriate)
|
|
//
|
|
|
|
if (PrimaryDomainSid != NULL && NT_SUCCESS(Status)) {
|
|
|
|
Status = RtlCopySid(SidLength,
|
|
PrimaryDomainSid,
|
|
PrimaryDomainInfo->Sid
|
|
);
|
|
}
|
|
} else {
|
|
|
|
(*PrimaryDomainPresent) = FALSE;
|
|
}
|
|
|
|
//
|
|
// We're finished with the buffer returned by LSA
|
|
//
|
|
|
|
IgnoreStatus = LsaFreeMemory(PrimaryDomainInfo);
|
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|
|
|
}
|
|
|
|
|
|
return(Status);
|
|
}
|