|
|
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
PSWUTIL.CPP
Abstract:
Keyring WinMain() and application support Author:
990917 johnhaw Created. georgema 000310 updated georgema 000501 used to be EXE, changed to CPL
Comments: !!!!! this file is a duplicate of a nearly identical file in the credui project. It should be removed when the implementation of NetUserChangePassword() is updated to handle unc names and MIT Kerberos realms properly. For now, it wraps NetUserChangePassword() to handle the extra cases.
Environment: WinXP
Revision History:
--*/ #include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <wincred.h>
#include <align.h>
#include <lm.h>
#include <ntsecapi.h>
#include <dsgetdc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Dependent libraries:
// secur32.lib, netapi32.lib
// external fn: NET_API_STATUS NetUserChangePasswordEy(LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR)
BOOL IsMITName ( LPCWSTR UserName ) { BOOL fReturn = FALSE; HKEY MitKey; DWORD Index; PWSTR Realms; DWORD RealmSize; int err; DWORD NumRealms; DWORD MaxRealmLength; FILETIME KeyTime; WCHAR *szUncTail; if (NULL == UserName) { return FALSE; } szUncTail = wcschr(UserName,'@'); if (NULL == szUncTail) { return FALSE; } szUncTail++; // point to char following @
err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Lsa\\Kerberos\\Domains"), 0, KEY_READ, &MitKey );
if ( err == 0 ) { err = RegQueryInfoKey( MitKey, NULL, NULL, NULL, &NumRealms, &MaxRealmLength, NULL, NULL, NULL, NULL, NULL, NULL );
MaxRealmLength++ ; MaxRealmLength *= sizeof( WCHAR );
Realms = (PWSTR) malloc(MaxRealmLength );
if ( Realms) { for ( Index = 0 ; Index < NumRealms ; Index++ ) { RealmSize = MaxRealmLength ;
err = RegEnumKeyEx( MitKey, Index, Realms, &RealmSize, NULL, NULL, NULL, &KeyTime ); if (err == 0) { if (0 == _wcsicmp(szUncTail, Realms)) { fReturn = TRUE; break; } } } } free(Realms); } return fReturn; }
NTSTATUS MitChangePasswordEy( LPCWSTR DomainName, LPCWSTR UserName, LPCWSTR OldPassword, LPCWSTR NewPassword, NTSTATUS *pSubStatus ) { HANDLE hLsa = NULL; NTSTATUS Status; NTSTATUS SubStatus; STRING Name; ULONG PackageId; PVOID Response = NULL ; ULONG ResponseSize; PKERB_CHANGEPASSWORD_REQUEST ChangeRequest = NULL; ULONG ChangeSize; UNICODE_STRING User,Domain,OldPass,NewPass;
Status = LsaConnectUntrusted(&hLsa); if (!SUCCEEDED(Status)) goto Cleanup;
RtlInitString( &Name, MICROSOFT_KERBEROS_NAME_A );
Status = LsaLookupAuthenticationPackage( hLsa, &Name, &PackageId ); if (!NT_SUCCESS(Status)) { // detect / handle failure to find kerberos
ASSERT(0); goto Cleanup; }
RtlInitUnicodeString( &User, UserName ); RtlInitUnicodeString( &Domain, DomainName ); RtlInitUnicodeString( &OldPass, OldPassword ); RtlInitUnicodeString( &NewPass, NewPassword );
ChangeSize = ROUND_UP_COUNT(sizeof(KERB_CHANGEPASSWORD_REQUEST),4)+ User.Length + Domain.Length + OldPass.Length + NewPass.Length ; ChangeRequest = (PKERB_CHANGEPASSWORD_REQUEST) LocalAlloc(LMEM_ZEROINIT, ChangeSize );
if ( ChangeRequest == NULL ) { ASSERT(0); Status = STATUS_NO_MEMORY ; goto Cleanup ; }
ChangeRequest->MessageType = KerbChangePasswordMessage;
ChangeRequest->AccountName = User; ChangeRequest->AccountName.Buffer = (LPWSTR) ROUND_UP_POINTER(sizeof(KERB_CHANGEPASSWORD_REQUEST) + (PBYTE) ChangeRequest,4);
RtlCopyMemory( ChangeRequest->AccountName.Buffer, User.Buffer, User.Length );
ChangeRequest->DomainName = Domain; ChangeRequest->DomainName.Buffer = ChangeRequest->AccountName.Buffer + ChangeRequest->AccountName.Length / sizeof(WCHAR);
RtlCopyMemory( ChangeRequest->DomainName.Buffer, Domain.Buffer, Domain.Length );
ChangeRequest->OldPassword = OldPass; ChangeRequest->OldPassword.Buffer = ChangeRequest->DomainName.Buffer + ChangeRequest->DomainName.Length / sizeof(WCHAR);
RtlCopyMemory( ChangeRequest->OldPassword.Buffer, OldPass.Buffer, OldPass.Length );
ChangeRequest->NewPassword = NewPass; ChangeRequest->NewPassword.Buffer = ChangeRequest->OldPassword.Buffer + ChangeRequest->OldPassword.Length / sizeof(WCHAR);
RtlCopyMemory( ChangeRequest->NewPassword.Buffer, NewPass.Buffer, NewPass.Length );
//
// We are running as the caller, so state we are impersonating
//
//ChangeRequest->Impersonating = TRUE;
Status = LsaCallAuthenticationPackage( hLsa, PackageId, ChangeRequest, ChangeSize, &Response, &ResponseSize, &SubStatus ); if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus)) { if (NT_SUCCESS(Status)) { Status = SubStatus; *pSubStatus = STATUS_UNSUCCESSFUL ; } else { *pSubStatus = SubStatus; } }
Cleanup:
if (hLsa) LsaDeregisterLogonProcess(hLsa);
if (Response != NULL) LsaFreeReturnBuffer(Response); if (ChangeRequest) { SecureZeroMemory(ChangeRequest,ChangeSize); LocalFree(ChangeRequest); } return(Status); }
/*
NetUserChangePasswordEy()
A wrapper function to superset the functionality of NetUserChangePassword(), specifically by adding support for changing the account password for an MIT Kerberos principal.
This routine accepts:
1. uncracked username, with NULL domain 2. cracked username, with domain portion routed to the domain argument
In case 1, it handles all cases, including MIT realm password changes In case 2, it will not handle MIT realms.
Case 2 is provided for backwards compatibility with NetUserChangePassword(). It is intended that callers should pass the uncracked name, and remove the cracking code from the client.
*/ NET_API_STATUS NetUserChangePasswordEy ( LPCWSTR domainname, LPCWSTR username, LPCWSTR oldpassword, LPCWSTR newpassword ) { NTSTATUS ns; // status from call
NET_API_STATUS nas; NTSTATUS ss; // substatus
#ifdef LOUDLY
OutputDebugString(L"NetUserChangePasswordEy called for "); OutputDebugString(username); OutputDebugString(L"\n"); #endif
// domainname may be a kerberos realm
// If not a UNC name, call through to NetUserChangePassword
// else
// locate UNC suffix
// search all domains returned by DsEnumerateDomainTrusts() for a match
// On match, if is kerberos realm, call MitChangePasswordEy()
// else call NetUserChangePassword
if ((domainname == NULL) && IsMITName(username)) { ns = MitChangePasswordEy(domainname, username, oldpassword, newpassword, &ss); // remap certain errors returned by MitChangePasswordEy to coincide with those of NetUserChangePassword
if (NT_SUCCESS(ns)) nas = NERR_Success; else { switch (ns) { case STATUS_CANT_ACCESS_DOMAIN_INFO: case STATUS_NO_SUCH_DOMAIN: { nas = NERR_InvalidComputer; break; } case STATUS_NO_SUCH_USER: case STATUS_WRONG_PASSWORD_CORE: case STATUS_WRONG_PASSWORD: { nas = ERROR_INVALID_PASSWORD; break; } case STATUS_ACCOUNT_RESTRICTION: case STATUS_ACCESS_DENIED: case STATUS_BACKUP_CONTROLLER: { nas = ERROR_ACCESS_DENIED; break; } case STATUS_PASSWORD_RESTRICTION: { nas = NERR_PasswordTooShort; break; } default: nas = 0xffffffff; // will produce omnibus error message when found (none of the above)
break; } } } else if (NULL == domainname) { WCHAR RetUserName[CRED_MAX_USERNAME_LENGTH + 1]; WCHAR RetDomainName[CRED_MAX_USERNAME_LENGTH + 1]; RetDomainName[0] = 0; DWORD Status = CredUIParseUserNameW( username, RetUserName, CRED_MAX_USERNAME_LENGTH, RetDomainName, CRED_MAX_USERNAME_LENGTH); switch (Status) { case NO_ERROR: { #ifdef LOUDLY
OutputDebugString(L"Non-MIT password change for "); OutputDebugString(RetUserName); OutputDebugString(L" of domain "); OutputDebugString(RetDomainName); OutputDebugString(L"\n"); #endif
nas = NetUserChangePassword(RetDomainName,RetUserName,oldpassword,newpassword); break; } case ERROR_INSUFFICIENT_BUFFER: nas = ERROR_INVALID_PARAMETER; break; case ERROR_INVALID_ACCOUNT_NAME: default: nas = NERR_UserNotFound; break; }
} else { // both username and domainname passed.
nas = NetUserChangePassword(domainname,username,oldpassword,newpassword); } #ifdef LOUDLY
WCHAR szsz[200]; swprintf(szsz,L"NetUserChangePasswordEy returns %x\n",nas); OutputDebugString(szsz); #endif
return nas; }
|