Leaked source code of windows server 2003
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.
 
 
 
 
 
 

657 lines
16 KiB

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1997
//
// File: userlist.cxx
//
// Contents: Routines for logging a client with a PAC onto an existing
// NT account.
//
//
// History: 21-Febuary-1997 Created MikeSw
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
#ifndef WIN32_CHICAGO
extern "C"
{
#include <samrpc.h>
#include <lsaisrv.h>
#include <samisrv.h>
}
#include <userall.h>
//+-------------------------------------------------------------------------
//
// Function: KerbReadRegistryString
//
// Synopsis: Reads & allocates a string from the registry
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns: STATUS_SUCCESS - the value was found
// STATUS_NO_SUCH_USER - the value was not found
// STATUS_INSUFFICIENT_RESOURCES - memory allocation failure
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbReadRegistryString(
IN HKEY Key,
IN LPWSTR Value,
IN OUT PUNICODE_STRING String
)
{
ULONG Type;
ULONG WinError = ERROR_SUCCESS;
ULONG StringSize = 0;
NTSTATUS Status = STATUS_NO_SUCH_USER;
WinError = RegQueryValueEx(
Key,
Value,
NULL,
&Type,
NULL,
&StringSize
);
if ((WinError == ERROR_MORE_DATA) || (WinError == ERROR_SUCCESS))
{
//
// The value exists - get the name from it
//
String->Buffer = (LPWSTR) KerbAllocate(StringSize);
if (String->Buffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
WinError = RegQueryValueEx(
Key,
Value,
NULL,
&Type,
(PUCHAR) String->Buffer,
&StringSize
);
if (WinError != ERROR_SUCCESS)
{
goto Cleanup;
}
RtlInitUnicodeString(
String,
String->Buffer
);
Status = STATUS_SUCCESS;
}
Cleanup:
if (!NT_SUCCESS(Status) && (String->Buffer != NULL))
{
KerbFreeString(String);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbMapClientName
//
// Synopsis: Maps a Kerberos client name to an NT user name. First it
// tries converting the name to a string and looking for a
// value with that name. If that fails, it looks for a
// value with the client realm name.
//
// Effects:
//
// Arguments: MappedName - receives the name of the local account the
// client maps to
// ClientName - Kerberos principal name of the client
// ClientRealm - Kerberos realm of the client
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbMapClientName(
OUT PUNICODE_STRING MappedName,
IN PKERB_INTERNAL_NAME ClientName,
IN PUNICODE_STRING ClientRealm
)
{
LPWSTR UserName = NULL;
DWORD WinError;
NTSTATUS Status = STATUS_NO_SUCH_USER;
HKEY Key = NULL;
UNICODE_STRING ClientString = {0};
EMPTY_UNICODE_STRING( MappedName );
//
// First convert the MIT client name to a registry value name. We do
// this by adding a '/' between every component of the client name
// and appending "@ClientRealm"
//
//
// Make sure the client realm is null terminated
//
DsysAssert(ClientRealm->Length == 0 || (ClientRealm->MaximumLength >= ClientRealm->Length + sizeof(WCHAR)));
DsysAssert(ClientRealm->Length == 0 || (ClientRealm->Buffer[ClientRealm->Length/sizeof(WCHAR)] == L'\0'));
//
// The value length is the length of all the components of the names,
// all the '/' separtors, and the name of the domain name plus '@'
//
if (!KERB_SUCCESS(KerbConvertKdcNameToString(
&ClientString,
ClientName,
ClientRealm)))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
D_DebugLog((DEB_TRACE,"Mapping client name %wZ\n",&ClientString ));
//
// Also build just the username, which is used when users are mapped
// back to their own name
//
UserName = (LPWSTR) KerbAllocate(ClientName->Names[0].Length + sizeof(WCHAR));
if (UserName == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlCopyMemory(
UserName,
ClientName->Names[0].Buffer,
ClientName->Names[0].Length
);
UserName[ClientName->Names[0].Length / sizeof(WCHAR)] = L'\0';
//
// Now check the registry for a mapping for this name
//
WinError = RegOpenKey(
HKEY_LOCAL_MACHINE,
KERB_USERLIST_KEY,
&Key
);
if (WinError != ERROR_SUCCESS)
{
goto Cleanup;
}
//
// Read out the value
//
Status = KerbReadRegistryString(
Key,
ClientString.Buffer,
MappedName
);
if (Status == STATUS_NO_SUCH_USER)
{
//
// Try again with just the domain name - this allows all users in
// a domain to be mapped
//
Status = KerbReadRegistryString(
Key,
ClientRealm->Buffer,
MappedName
);
}
if (Status == STATUS_NO_SUCH_USER)
{
Status = KerbReadRegistryString(
Key,
KERB_ALL_USERS_VALUE,
MappedName
);
}
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// If the mapped name is '*', then use the client's name
//
if (_wcsicmp(MappedName->Buffer,KERB_MATCH_ALL_NAME) == 0)
{
KerbFree(MappedName->Buffer);
RtlInitUnicodeString(
MappedName,
UserName
);
UserName = NULL;
}
Status = STATUS_SUCCESS;
Cleanup:
if (!NT_SUCCESS(Status))
{
KerbFreeString(MappedName);
}
KerbFreeString(&ClientString);
if (UserName != NULL)
{
KerbFree(UserName);
}
if (Key != NULL)
{
RegCloseKey(Key);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbCreatePacForKerbClient
//
// Synopsis: Creates a PAC structure for a kerb client without a PAC
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbCreatePacForKerbClient(
OUT PPACTYPE * Pac,
IN PKERB_INTERNAL_NAME ClientName,
IN PUNICODE_STRING ClientRealm,
IN OPTIONAL PUNICODE_STRING MappedClientName
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
SAMPR_HANDLE SamHandle = NULL;
SAMPR_HANDLE DomainHandle = NULL;
SAMPR_HANDLE UserHandle = NULL;
PSAMPR_GET_GROUPS_BUFFER Groups = NULL;
SID_AND_ATTRIBUTES_LIST TransitiveGroups = {0};
PSAMPR_USER_INFO_BUFFER UserInfo = NULL;
PPACTYPE LocalPac = NULL;
SAMPR_ULONG_ARRAY RidArray;
SAMPR_ULONG_ARRAY UseArray;
SECPKG_CLIENT_INFO ClientInfo;
//
// local variables containing copy of globals.
//
UNICODE_STRING LocalMachineName;
UNICODE_STRING LocalDomainName;
UNICODE_STRING LocalAccountName = NULL_UNICODE_STRING;
KERBEROS_MACHINE_ROLE LocalRole = KerbRoleWorkstation;
BOOLEAN GlobalsLocked = FALSE;
RidArray.Element = NULL;
UseArray.Element = NULL;
LocalMachineName.Buffer = NULL;
LocalDomainName.Buffer = NULL;
//
// Verify that the caller has TCB privilege. Otherwise anyone can forge
// a ticket to themselves to logon with any name in the list.
//
Status = LsaFunctions->GetClientInfo(&ClientInfo);
if (!NT_SUCCESS(Status))
{
return(Status);
}
if (!ClientInfo.HasTcbPrivilege)
{
return(STATUS_PRIVILEGE_NOT_HELD);
}
//
// If we are a domain controller, call SAM to do the mapping.
// Otherwise, do it ourselves.
//
//
// Common code for both wksta and DC - open SAM
// However, if we're a realmless wksta, we know we have a client
// mapping to a local account so skip lookup on DC
//
//
// Call the LSA to get our domain sid
//
Status = LsaIQueryInformationPolicyTrusted(
PolicyAccountDomainInformation,
&PolicyInfo
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Open SAM to get the account information
//
Status = SamIConnect(
NULL, // no server name
&SamHandle,
0, // no desired access
TRUE // trusted caller
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = SamrOpenDomain(
SamHandle,
0, // no desired access
(PRPC_SID) PolicyInfo->PolicyAccountDomainInfo.DomainSid,
&DomainHandle
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// grab the globals while holding the lock.
// ... then release the lock prior to making the call!
//
KerbGlobalReadLock();
GlobalsLocked = TRUE;
LocalRole = KerbGlobalRole;
Status = KerbDuplicateString( &LocalMachineName, &KerbGlobalMachineName );
if(!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalMachineName\n"));
goto Cleanup;
}
if( LocalRole == KerbRoleDomainController )
{
Status = KerbDuplicateString( &LocalDomainName, &KerbGlobalDomainName );
if(!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalDomainName\n"));
goto Cleanup;
}
}
KerbGlobalReleaseLock();
GlobalsLocked = FALSE;
//
// If the is a DC, try to look up the name in SAM as an AltSecId.
// If that fails, we will try looking at the registry mapping.
//
if (LocalRole == KerbRoleDomainController)
{
UNICODE_STRING AltSecId = {0};
KERBERR KerbErr;
KerbErr = KerbBuildAltSecId(
&AltSecId,
ClientName,
NULL, // no unicode realm
ClientRealm
);
if (!KERB_SUCCESS(KerbErr))
{
Status = KerbMapKerbError(KerbErr);
goto Cleanup;
}
Status = SamIGetUserLogonInformationEx(
SamHandle,
SAM_OPEN_BY_ALTERNATE_ID,
&AltSecId,
USER_ALL_PAC_INIT,
&UserInfo,
&TransitiveGroups,
NULL // no user handle
);
KerbFreeString(&AltSecId);
}
if (!NT_SUCCESS(Status) || (UserInfo == NULL))
{
if (!ARGUMENT_PRESENT(MappedClientName) || MappedClientName->Buffer == NULL)
{
Status = KerbMapClientName(
&LocalAccountName,
ClientName,
ClientRealm
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
else
{
KerbDuplicateString(
&LocalAccountName,
MappedClientName
);
}
Status = SamrLookupNamesInDomain(
DomainHandle,
1,
(PRPC_UNICODE_STRING) &LocalAccountName,
&RidArray,
&UseArray
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
if ((UseArray.Element[0] != SidTypeUser) &&
(UseArray.Element[0] != SidTypeComputer))
{
Status = STATUS_NONE_MAPPED;
goto Cleanup;
}
//
// Finally open the user
//
Status = SamrOpenUser(
DomainHandle,
0, // no desired access,
RidArray.Element[0],
&UserHandle
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = SamrQueryInformationUser(
UserHandle,
UserAllInformation,
&UserInfo
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = SamrGetGroupsForUser(
UserHandle,
&Groups
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
//
// This is common code
//
//
// Set the password must changes time to inifinite because we don't
// want spurious password must change popups
//
UserInfo->All.PasswordMustChange = *(POLD_LARGE_INTEGER) &KerbGlobalWillNeverTime;
//
// Finally build the PAC
//
Status = PAC_Init(
&UserInfo->All,
Groups,
&TransitiveGroups, // no extra groups
PolicyInfo->PolicyAccountDomainInfo.DomainSid,
((LocalRole == KerbRoleDomainController) ?
&LocalDomainName : &LocalMachineName),
&LocalMachineName,
0, // no signature
0, // no additional data
NULL, // no additional data
&LocalPac
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
*Pac = LocalPac;
LocalPac = NULL;
Cleanup:
if( GlobalsLocked )
{
KerbGlobalReleaseLock();
}
KerbFreeString( &LocalMachineName );
KerbFreeString( &LocalDomainName );
KerbFreeString( &LocalAccountName );
if (UserHandle != NULL)
{
SamrCloseHandle( &UserHandle );
}
if (DomainHandle != NULL)
{
SamrCloseHandle( &DomainHandle );
}
if (SamHandle != NULL)
{
SamrCloseHandle( &SamHandle );
}
if (Groups != NULL)
{
SamIFree_SAMPR_GET_GROUPS_BUFFER( Groups );
}
SamIFreeSidAndAttributesList(&TransitiveGroups);
if (UserInfo != NULL)
{
SamIFree_SAMPR_USER_INFO_BUFFER( UserInfo, UserAllInformation );
}
if (PolicyInfo != NULL)
{
LsaIFree_LSAPR_POLICY_INFORMATION(
PolicyAccountDomainInformation,
PolicyInfo
);
}
SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
SamIFree_SAMPR_ULONG_ARRAY( &RidArray );
if (LocalPac != NULL)
{
MIDL_user_free(LocalPac);
}
return(Status);
}
#endif // WIN32_CHICAGO